mysql分表真得能提升查询性能吗

首先将存储引擎限定在innodb

2种方式分表

  1. 水平分表,根据某一列或者某几列将表按行分割到多张表中,达到减少每张表行数的目的
  2. 垂直分表,将表的一些列拆分到多张表中,达到减少每张表列数的目的

分表真得能提升查询性能吗

分表分库

如果将不同的表分到多个库多个机器上,那一定是能提升性能的,毕竟你花钱买机器了,总的可用CPU和内存高了。这种情况不必考虑。

分表不分库

如果所有分表都在同库同机器上,每个表的查询都共用CPU和内存,性能是怎么提升的?

1. 有索引的情况

例如这条查询 select * from user_feeds where uid = ? ,且uid字段上有二级索引,查询步骤为:

  1. 确定索引的扫描范围,即找到B+树上uid=X的最左叶子节点和最右叶子节点,可能(不考虑已经缓存到buffer pool中)需要进行的最大IO次数为4(4层b+树已经可以存放巨量的数据了,一般千万级的是3层)
  2. 遍历扫描范围内的索引数据,取出主键id
  3. 通过主键id进行回表查询,IO次数最大值为扫描范围内索引记录数量乘以聚簇索引中定位一条记录的IO次数(B+树层数)

通过以上步骤可以看出,查询的主要成本为:确定索引扫描范围的IO次数(最大为4)、遍历扫描范围内索引数据的CPU占用(与扫描范围内记录数成正比)、回表IO次数(与扫描范围内记录数成正比,且受聚簇索引B+树层数影响)。所以查询的成本主要取决于uid=X对应的记录数量和索引B+树的层数。

来看一下分表后的查询成本:

  1. 首先是确定索引扫描范围的IO次数由B+树的层数决定,大部分情况下分表是会降低单表的树的层数,例如由4层降到3层,可以减少一次IO
  2. 扫描范围内记录数不会变,所以遍历扫描范围内索引数据的CPU占用不变
  3. 回表IO次数,由于扫描范围内的记录数不变,回表次数也不变,通过聚簇索引定位具体记录数据IO次数可能会减少(同步骤1中普通索引确定扫描范围的IO次数同理)

可见分表后主要的差别在于确定索引扫描范围的IO次数和回表IO次数,即分表后B+树的层数变化,变化范围为0到3。对于不需要回表的查询,差别在于用到的索引B+树层数变化导致的确定扫描范围的IO次数变化;对于需要回表的查询,除了确定扫描范围的IO次数变化,还有回表IO次数变化(回表IO次数变化=记录数X聚簇索引B+树层数变化)。

2. 全表扫描的情况

以上是有二级索引的情况,如果没有索引,需要全表扫描时,扫描整个聚簇索引的成本:加载数据页到内存的IO次数(聚簇索引的页面数)、扫描记录的CPU占用(整个聚簇索引的记录数量)。分表以后所有表总的记录数量不变,页面数量也可以认为不变。所以全表扫描的查询性能分表与不分表也不会有什么差别。

3. 垂直分表能带来性能提升吗

垂直分表,表记录数不会改变,每条记录数据占用空间会变小,从而导致单表聚簇索引每个数据页可以存储的记录变多,聚簇索引的数据页数量变少,原来uid=X的记录可能分布在N个数据页上,分表后uid=X的记录分布在的数据页数量<=N。也就是减少回表IO的次数。

但是,这种情况只限于合理的垂直分表,也就是select的列可以落在同一张表中。

4. 分表对写操作的性能提升(待续)

innodb索引数据大小估算

理论估算

索引列:uid、created_at
单条记录大小 uid4字节、created_at4字节、主键post_id8字节,页号6字节 共22字节
单页可以存放的记录数 16kb(16384)/22 = 744,去除掉一些其他信息(header、槽信息),再去除一些碎片(删除造成的、没填满造成的),姑且认为每页存放500条记录

如果总的记录条数为5000w,那么需要的叶子节点数量为 5000w/500 = 10w,B+树需要两层(500X500 = 25w)
索引叶子节点占用空间大小为 10wX16kb = 1.6GB
索引目录节点占用空间大小为 500X16kb = 8M

实际业务场景中

测试环境

单条记录大小 24
单页可以存放的记录数 682

与理论估算相近

// innodb统计信息查询方法,注意sum_of_other_index_sizes代表的是所有二级索引占用的页面数
select n_rows,'Secondary Indexes' AS 'BTrees',sum_of_other_index_sizes * 16384 AS Bytes,ROUND(sum_of_other_index_sizes * 16384 / n_rows) AS 'Bytes/row',sum_of_other_index_sizes AS Pages,ROUND(n_rows / sum_of_other_index_sizes) AS 'Rows/page' FROM innodb_table_stats where database_name = 'stt_post' and table_name = 'posts'
生产环境有限的信息
99.png

索引空间大小19.38GB,生产环境二级索引数量16个,平均每个索引空间大小约1.1GB,比理论估算还小一些。

你可能感兴趣的:(mysql分表真得能提升查询性能吗)