TiDB 单表200亿如何提高性能?

 CREATE TABLE `test` (
  `id` varchar(32) NOT NULL,
  `no` varchar(50) DEFAULT NULL,
  `mode` varchar(50) NOT NULL,
  `type` varchar(50) NOT NULL,
  `createTime` datetime NOT NULL,
  `updateTime` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`,`createTime`),
  KEY `idx_createTime` (`createTime`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

类似这样的表,还有一些列,大概单表200亿,每天的数据量大概能在1~4亿浮动。
类似的 sql:

SELECT count(1) FROM test  WHERE  createTime BETWEEN '2024-3-24 00:00:00' AND '2024-3-24 23:59:59' and  no= '132943828293'

查询速度在分钟级别,以前是用分库分表,查询速度能5秒内,打算换tidb,这个情况不用tiflash是不是就是查不快?怎么优化能让tidb的速度快到10秒内?

分库分表能均匀的把请求发到所有机器上,tidb因为都是region,自由调度,一天的数据可能就集中在一两台机器上。如果说查询的qps比较低的话,不太可能把所有tikv都压起来,结果就是只有两三台tikv参与这次查询,速度就比较慢。这个有什么优化的方法吗?

以createTime 按天range分区

分区表性能比不分区快吗?分区表相当于把一张大表分成很多小表,有时间索引的情况下,大表扫描的数据量会很多吗?

分区裁剪,相当于扫描的数据直接少了很多(比索引更快)
https://docs.pingcap.com/zh/tidb/stable/partition-pruning#分区裁剪

v6.5以后有动态分区裁剪,tiflash机器性能好,也可以扫的动

如果TiDB以后也像Oracle支持本地分区索引,还会更快 :upside_down_face:

假设共200亿,一天的数据就1亿,不分区的话,带索引查询,扫描1亿的索引+回表1亿是吗?
分区表不也是扫描1亿的索引+1亿的回表?
还是说不分区的时候扫描的范围大于一亿?

说实话,你这表,可能对整个系统稳定性有影响。这个表需求方数仓库里查吧!

如果是分区表,应该是没有1亿的回表(因为直接选择分区读数据,没有索引)

是典型的聚合计算,tiflash+mpp确实是最好的办法。

我给你一个参考。这是在一个2.9亿的分区表上,执行类似的count(1)的执行计划。

1711521888264

硬件配置是2台 4c8g的机器,2 tiflash实例+mpp。

io比较次,是腾讯云,最便宜的存储。cpu你可以看到,波动还不如10分钟一次的gc。

行存上针对性的建立索引也是个解决方案。问题在于where的条件真的是比较确定的嘛?如果不是,那索引一旦没命中的效率就很差了。

[quote=“TiDBer_jYQINSnf, post:1, topic:1023642”]

SELECT count(1) FROM test  WHERE  createTime BETWEEN '2024-3-24 00:00:00' AND '2024-3-24 23:59:59' and  no= '132943828293'

看这个sql,当天的数据要全都查一遍,no没有索引。这种情况下回表就是一亿吧。不分区和分区的区别在于扫描索引的时候的条数,不分区扫描的索引条数比分区扫描的索引条数多吗?会多余1亿吗?

理论上是这样的
先通过createtime索引扫1亿,然后回表1亿
分区表的话,直接落在partition上扫1亿

用tiflash应该会比较快,我是想了解下不用tiflash还有办法吗。

where条件确定,那就是索引。
不过我的经验是大部分都不会确定的。哪怕研发现在信誓旦旦。最后索引没命中,tikv cpu狂飙,全表扫描,依然需要你来收拾。

用分区表,也有扫描索引,这个IndexRangeScan比不分区的时候读取的行数会多吗?

mysql> explain SELECT count(1) FROM test  WHERE  createTime BETWEEN '2024-3-27 00:00:00' AND '2024-3-27 23:59:59' and  no = '12314321231';
+----------------------------------+---------+-----------+---------------------------------------------------------------+---------------------------------------------------------------------------------+
| id                               | estRows | task      | access object                                                 | operator info                                                                   |
+----------------------------------+---------+-----------+---------------------------------------------------------------+---------------------------------------------------------------------------------+
| StreamAgg_12                     | 1.00    | root      |                                                               | funcs:count(1)->Column#31                                                       |
| └─IndexLookUp_28             | 0.25    | root      |                                                               |                                                                                 |
|   ├─IndexRangeScan_25(Build) | 250.00  | cop[tikv] | table:test, partition:p202403, index:idx_createTime(createTime) | range:[2024-03-27 00:00:00,2023-03-27 23:59:59], keep order:false, stats:pseudo |
|   └─Selection_27(Probe)      | 0.25    | cop[tikv] |                                                               | eq(test.test.no, "12314321231")                               |
|     └─TableRowIDScan_26      | 250.00  | cop[tikv] | table:test, partition:p202403| keep order:false, stats:pseudo                                                  |
+----------------------------------+---------+-----------+---------------------------------------------------------------+---------------------------------------------------------------------------------+
5 rows in set (0.00 sec)

感觉这个表不加tiflash搞不定,但是加上tiflash又得>1副本,那就5副本了。成本没优势 :man_shrugging:

1 个赞

你说的是一个非常现实的问题。
好比我这2个如此低配的tiflash节点都是和别的系统挤一台的,原因无非也是成本。

我感觉,你可以找个配置很差的机器先尝试用起来,也许用起来就有人愿意为它的稳定付费了。
如果没人在乎,占的成本也不多。
我觉得4c8g这种配置部署一个,应该还是可以接受的。只能说尽力尝试一下,万一提升能出些报告,成本问题也就有希望解决了。

:joy:那就是tidb在分区键上会自动加个索引,那和直接建索引没区别了

SELECT count(1) FROM test  WHERE  createTime BETWEEN '2024-3-24 00:00:00' AND '2024-3-24 23:59:59' and  no= '132943828293'

你这个sql难道不是建一个no和createTime的联合索引就行吗?

1 个赞

no这一个列没啥区分度,就几个值。这个sql也是举个例子,实际上是针对这张表做了一个表单,每一列一个单元格,能控制的是强制输入日期,其他列任意组合,超时时间30秒。
用分库分表查询还是凑合能用,用tidb替代的话,好像有点难度。