一,一个技术人常被问到的问题
Q:一张表数据量达到多大才需要进行分表的操作?
A:一些八股文的说法是,达到xxx条数据就需要进行分表的操作了
通常我们会使用一个简单的指标 - “数据量”,来判断是否需要开始进行分表操作,如上面这个问题
很明显,在不同条件下这个问题会有不同的回答,并不能一概而论
通常大部分人认为,数据量的大小会明显影响查询的耗时
所以首先要明白一个问题,数据量越大,查询就会越慢吗?
看下面这个例子:
1,mysql5.7下创建四张表, 分别插入 20 万 , 50万 , 150万, 1500万数据
CREATE TABLE `t1` ( `id` int(11) NOT NULL, `c` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE `t2` ( `id` int(11) NOT NULL, `c` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE `t3` ( `id` int(11) NOT NULL, `c` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE `t4` ( `id` int(11) NOT NULL, `c` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
create procedure idata1()
begin
declare i int;
set i=1;
while(i<=200000) do
insert into t1 values(i,i);
set i=i+1;
end while;
end;;
call idata1();
# 同样的方法再给t2 , t3, t4 插入数据
# 也可以使用load data的方式导入,会快很多,这里不再讨论
分别对这几张表执行相同的查询sql, 观察各自的执行时间
测试过程中最好将query_cache_type 设置为OFF ,防止重复执行的结果被缓存干扰
如果你是8.0以上的mysql,默认去除了查询缓存模块,可以不用设置,mysql5.7版本默认也是OFF的
使用 show variables like '%query_cache_type%'
查看变量的值
使用 **set GLOBAL **query_cache_type=0;
** 关闭缓存功能**
2, 在没有添加索引的情况下, 相同的sql,分别执行了(80ms, 130ms, 430ms, 3550ms)
可以看到,似乎数据量越大,查询的耗时越多?
那么我们加上索引呢? 索引对于查询的优化效果不言而喻,我们分别给四张表加上c字段索引,再次查询
可以发现,加上索引后,在查询速度上几乎没有什么区别,都非常快
我们可以得出一个结论:
对于可以命中或者使用索引的查询,无论表大小,对查询速度的影响微乎其微
对于无法命中或者使用索引的查询,表的数据量越大,对查询速度的影响越大
当然,一切都建立在合理的索引与数据结构之上
虽然以上的案例明显没有达到单表的极限,但我们依然可以总结出分表的使用场景:
(1)可以通过sql优化手段解决的问题都没必要进行分表等复杂操作
(2) 单表数据量过大,且无法通过其他手段进一步优化,这个时候,才需要考虑分表
二,从底层数据结构,分析单表的瓶颈
上面说到单表数据量过大,而且无法通过常规的手段进行优化的情况,我们需要从底层的存储结构进行分析
回顾一下innodb的相关概念:
- innodb使用b+树模型对数据进行存储,每一条索引对应一颗b+树,每颗树由叶子节点和非叶子节点组成,前者只做索引使用,后者存放具体的数据
- 聚簇索引和非聚簇索引:区别是前者的叶子节点会存放整条数据的所有字段的值,后者除了存放索引字段的值外,只会存主键的值,主键索引是聚簇索引,其他索引都是非聚簇索引
- 在执行查询语句时,会根据b+树的结构一步步向下查找,主要的耗时集中在每一次的磁盘io(也就是将非叶子节点的数据load到内存中的耗时,根据索引内容查找下一个节点的耗时相比之下可以忽略不计)
不难理解,随着树的增高,每次查询都会增加磁盘io的次数,导致速度变慢
通常情况下b+树的高度在3层,尽量确保每次查询的磁盘io的次数在 2 次,(根结点会被load到内存中常驻),以便保持最佳性能
那么** **
一颗高度为3的b+树,最多能够保存多少数据呢?
innodb页文件: innodb的最小储存单元,也称为页,每一个后缀为 ibd 的文件,默认大小都为16k (也可以指定为64k,对应变量名为 innodb_page_size , 这里不做讨论),所有数据都存放在这样的文件中
上面说过,非叶子节点存放的是索引数据,具体包括(索引键值+指向下一个节点的指针),如果这里的键为bigint类型,占用8字节,innodb指针占用6字节, 总共 14 字节, 那么可以算出,一个页文件可以存放
16*1024 / 14 = 1170 个非叶子节点, 每个非叶子节点作为索引指向一个页文件
可以得出,共有 1170 * 1170 个页文件可以存放具体的数据,每个页文件为16k,如果一条数据预估为1k,那么一个页文件可以存放16条数据
所以得到结论, 一个高度为 3 的b+树, 主键为bigint(8字节), 单条数据大小为1kb, 最多可以存放:
((16*1024)bytes / (8+6)bytes)**2 * (16 / 1) = 1170**2 * 16 = 21902400
大约2000W条数据
一般结构比较简单的表,单条数据也很少会达到1kb的大小,所以我们经常会发现,即便一些表数据量达到几亿速度也依然不会被影响,这里针对的是主键索引对应的b+树
如果是一颗联合索引的b+树,很显然,在数据量增多的后,可能会提前写满数据或者使树变高,从而导致查询性能降低
二,总结
对于是否需要进行分表:
在现有手段能够优化性能的时候,没必要进行分表操作,分表是在数据量达到单表瓶颈的时候所采用的优化手段
在保持最大性能的前提下,可以提前预估单表的最大数据量:
在保证树高越矮的前提的下,依据业务表结构预估出单表最大数据条数,可以提前制定好分表计划
依据表字段,表结构,索引等是否完善,并与当前的业务,表数据量结合起来分析,判断你是否需要分表,以及如何进行分表
标签:到底,int,查询,索引,innodb,数据量,分表 From: https://www.cnblogs.com/xiuneng/p/17082266.html