导读
以交友平台用户中心的user表为例,单表数据规模达到千万级别时,你可能会发现使用用户筛选功能查询用户变得非常非常慢,明明查询命中了索引,但是,部分查询还是很慢,这时候,我们就需要考虑拆分这张user表了。
如果此时,我们才去做分表,可能已经太晚了,为什么呢?
我以最典型的应用场景:用户筛选功能,以查询年龄在18到24岁的100位女性用户为例:
在单表的情况下,我们的SQL是这么写的:
SELECT * FROM user WHERE age >= 18 AND age <= 24 AND sex = 0 LIMIT 100
复制代码
但是,拆分user表后,用户记录分散到了多张表,比如,分散到user_1
,user_2
,user_3
这三张表,此时,要查询满足上面条件的用户,我们的查询过程就变成这样:
- 遍历
user_1
到user_3
这三张表 - 分别从三张表找出满足条件的用户,即执行上面的SQL
- 合并这些用户记录
- 从合并结果中过滤出前100名用户记录
通过对比,我们会发现分表后的查询过程跟单表相比,变化是比较大的,这势必导致我们不得不修改代码,如果系统内类似的情况很多,那么,可能引发系统较大规模的业务逻辑改动,所以,在系统真正出现数据库性能瓶颈前,必须提前规划分表方案,预留时间去做系统改造。
那么,问题来了,我们到底在单表数据规模达到多少时,做分表是最合适的呢?
在开头我提到分表的原因是因为单表数据规模太大,导致系统功能使用越来越慢,而影响数据库查询性能的因素很多,有并发连接线程数、磁盘IO,锁等等。但是,一条查询语句如果需要通过磁盘IO来获得查询结果,那么,无论是否存在数据库的并发查询请求,磁盘IO的性能瓶颈都会存在。而连接线程和锁导致的的性能问题,一般只有在高并发的场景下才会出现。所以,减少数据查询的磁盘IO,是我们在优化数据库查询性能时,最先需要考虑的。
那么,MySQL又是通过什么方法来减少数据查询的磁盘IO的呢?我们来看下面这张图:
这是很典型的应用请求MySQL的示意图,从图中,我们很容易发现,MySQL为了避免查询时都从磁盘读取查询结果,所以,在磁盘和应用之间加了一层内存,尽可能将磁盘数据加载到内存,那么,下次查询请求访问MySQL时,可以从内存中获取查询结果,避免了过多的磁盘IO的读取。
所以,通过MySQL对磁盘IO的优化方案,我们可以看出,只要把表中大部分数据缓存在内存中,那么,数据库的查询性能可以大大提升。结合user表
来看,只要user表
的数据规模可以保证大多数的数据可以加载到内存,那么,就不需要对user表
拆分,反之,则需要拆分。
既然MySQL内存的大小决定了表何时拆分,那么,我们就先来看一下MySQL的内存结构吧!
内存管理
MySQL的内存结构: 整个MySQL的内存主要分为3部分:
Thread Memory:这部分内存空间是每个连接线程独享的,也就是说每个连接自身独立拥有自己的内存空间。连接释放时,