?????? 欢迎点赞收藏关注哟
一. 前言
数据库作为整个系统的一大件,在秒杀场景下同样有很多需要考虑的地方。
这一篇先从宏观角度来看,不谈具体的优化细节。能用钱解决的,就先不考虑小优化。
文章主题
分库分表并不是并发场景标准解决方案,但是就像标题里面说的 :最合适。
-
首先
单库 的性能其实没我们想的那么差 ,如果 愿意花钱, Oracle 数据库扛百亿数据一点问题没有。我见过给数据库2T内存 ,什么功能实现不了。 -
其次使用特别的存储介质其实效果能更好 ,把热点数据放 Redis ,把订单数据放 MongoDB , 用 ES 做索引查询,甚至于上图数据库,列数据库 ,在一些特殊的场景其实最终的效果能更好。 -
但是呢,这些 NoSQL数据库 需要更多的资源,
在人力,学习上面都有更多的成本 。 -
所以 ,综合成本和经济 ,以及扩展和切换等因素的情况下,通过分库分表来存储大数据量的同时保证性能就是最合适的了。
二. 深入理解分库分表的目的
2.1 分表是为了解决什么问题?
也许你还在用单体数据库
首先要有一个清晰的认知,
以 MySQL 为例 ,上亿的数据
演化
如果单机无法解决,那么解决思路就往多台服务器上面去想。简单的方式有以下几种 :
-
方案一 :
代码层面 去定制多个数据库,也就是通过调用不同的 DataSource 就可以简单实现对多库的调用 -
方案二 :
读写分离 ,将读取的压力分流到其他的读库,从而提高系统的整体并发能力。 -
方案三 : 结合
其他存储介质 (Redis缓存,ES全文索引),来实现功能的分流
突破
当量达到一定的地步,上述的方案还是会遇到瓶颈,
这个时候,就要考虑分库分表了。这里只针对 MySQL 来说 , 在我看来分库分表有技术层面和逻辑层面2种 :
-
逻辑层面 : 大库分小库,基于业务微服务划分多个库。 宽表改小,关联性的数据放在其他的表,减少单表的体积。
-
技术层面 :这个层面的分库分表就是一般我们说的 ,把一个表分成 库A/B/C/D , 表分成 01/02/03/04 ,
通过分库来释放当实例的压力,再通过分表来释放单表的上限 。
2.2 分库分表该怎么设计
下面来主要针对技术层面的分库分表进行讨论,一般业界对于如何分有两大方案,其核心都是通过一定的计算来将数据指向不同的库/表 (后面我统一称为分区)
-
方案一 :Hash分区 ,基于特定主键进行 Hash
取模 分库数量(分表数量),从而确定数据具体应该落在哪个分区里面 -
方案二 :范围分区 ,通过特定主键进行
数值切分 ,从而得到对应的分区 ,例如 1-10万到库A ,10万到20万到库B
一般对项目的建议都是早做分表,原因在于后续迁移带来很大的困难。但是难点也很明显,初创公司没有谁能预知未来的并发情况,所以一般会有几种设计方式 :
直接选择范围分库
这种类型可以有2种处理方式 :
- 方式一 : 通过 ID 进行分库 ,就像上面说的 0-100万库A ,100万-200万库B。
-
好处就是可以一直横向扩展,且每个表的数据总量比较均衡
-
坏处就是数据会耦合,可能热点数据始终在后面的库,前面的库又没办法拿掉,数据操作不均衡
-
- 方式二 : 通过 Domain 等租户ID分库 ,这种更适合 Saas 系统,租户A在A库,租户B被分到B库
-
好处是租户的数据通常可以聚合在一个实例里面
-
坏处是数据分布会不均衡,可能租户A的数据量很大,租户B就几个用户
-
通过 Hash 分库
通过 Hash 分库分表应该是最常见的方式,使用的时候选择一个字段作为分片键,通过对这个字段进行 Hash 后取模,平均分配到不同的数据分片中。
使用 Hash 方式的情况下,数据是很均衡的 。但是同样要考虑后期扩展问题,也有多种方案 :
-
早期场景数据量不大的情况下,可以考虑数据由单库迁移到多库。当然不是自己写程序迁移 ,一般都是走双写,有的云厂商还会提供迁移工具使用,两者数据一致后,进行数据库切换。 因为数据量不大,所以难度不高。
-
而针对发展比较迅速的企业,可以直接一开始就进行分库分表(比如直接建好64个分表丢到不同库中),但是为了节省资源,可以在一个实例上建多个库,等数据量大了之后,再将整个库进行迁移即可。 虽然还是要迁移 ,但是复杂度要低很多。
2.4 分库分表有什么难点
-
?难点一 : 分库分表时很多语法是不支持的
这种特性尤其在使用 Mycat 和 云厂商的时候尤为明显,Mycat 原理是做了数据库代理,相当于把 Client 的 MySQL 请求接收到后 转发到实际的 MySQL 中,这就导致版本上就会带来很多函数的不匹配。
而对于云厂商的数据库,通常都不是原生MySQL , 有的是进行了改造,有的更是自研后适配了 MySQL 协议,语法自然不支持。
-
?难点二 :连表查询会很困难
因为涉及到了多个表,所以必然会带来一些连表查询的问题。最核心的点在于没有命中分片键或者连表导致分片键不生效的时候,就会进行分区多表扫描。
-
?难点三 : 事务处理复杂
以 MySQL 为例,当单个库的时候,很简单的 Begin + Commit 就实现了事务。当多库的时候,其本质上就演变成了分布式事务的场景,由此就会带来数据操作的复杂性。当事务处理问题的时候,回滚也会很复杂。
像 Mycat ,其只支持弱 XA ,而我在使用 MyCAT 的时候,也尽量会控制事务的粒度,避免复杂的事务出现。
-
?难点四 : 后期扩展会比较麻烦
一般分库分表的后期扩展难免需要进行数据迁移,归根的方案大多数都是空闲迁移 + 双写。
-
?难点五 : 分库分表也不仅仅是处理数据库
虽然我们做了数据库的分库分表,但是在一些业务的设计上还要考虑其他的环节 ,例如 Redis 缓存等。
这些东西同样会在分库分表中影响某个环节的最终性能。这个时候就要考虑逻辑上进行
具体来说就是逻辑分桶后的数据处在自己的一套数据集里面,并不会和其他的数据集打角度。这个数据集从缓存到数据库应该都是完善和独立的。
三. 具体的实现方案
3.1 Mycat 分表方式
关于 Mycat 的使用方式我这里就不写了,之前也只写过两篇其他的 :
# MyCat : 主流程学习与源码入门[4]
# Mycat : Mycat 作为代理服务端的小知识点[5]
MyCAT 对于应用系统来说就是一个数据,应用系统可以用常见的数据库连接协议去发起调用(JDBC / MySQL客户端)。
然后在 Mycat Server 进行分库分表的处理 , 包括
Mycat 有什么优势,又有什么问题?
下面就来宏观的分析一下 Mycat 的好处和问题。 Mycat 是通过数据库代理来实现分库分表的。
- 优势 :
-
开发成本低 ,调用方式和调用 MySQL 没有区别,用户只需要在 Zookeeper 中进行 Mycat 分库的配置就能快速实现分库 -
分库分表运算的
压力不在应用系统中 , 将应用系统 和 分库分表处理器 进行解耦 (PS :如果需要可以部署 Mycat 集群来减轻单台的压力) -
Mycat 数据查询带来的负载并不会影响到应用系统,
对风险进行了分摊 -
通过 Navicat 等工具进行表数据查询的时候会简单的多,就想查询一个系统一样
-
- 问题 :
-
黑盒 ,黑盒 还是黑盒 ,Mycat 的特点就导致了如果 Mycat 出现问题,分析和解决起来都特别蛮烦 -
生态收缩 , Mycat 第一代差不多发布到了 1.6 就截止了 ,Mycat 2.0 的使用要远低于一代,生态现在要差很多 -
对事务的支持弱 ,定制困难。 mycat 本身基于 XA 的弱事务,加上内聚到了 Mycat Server 里面 ,就导致事务其实只能按照这一套去玩了
-
3.2 Sharding 分表方式
Sharding 我本身也用了一段时间,和 Mycat 对比 ,Sharding JDBC 将分表的逻辑放在了客户端,也就是应用系统里面
# 盘点Sharding-JDBC : 读写分离[6]
# 盘点Sharding-JDBC : 分库分表[7]
- 优点 :
-
Sharding 相当于将分表的运算和汇总放在了应用系统这个客户端来做,
均摊压力到客户端 -
分表处理在客户端后 ,
问题的排查会简单的多 ,切面,日志,代理都可以安排上 -
分布式事务的处理要相对简单一点
-
- 缺点 :
-
分表的处理方式会直接
影响到应用的性能 -
业务查询的时候不是很好处理
-
相对而言,写 ShardingJDBC 的分表规则回比写 Mycat 复杂些,或许可以说自定义的能力强了点,就牺牲了简洁性
-
3.3 一些云厂商的分表方式
云上的分表方式也有一些,我接触的主要是案例的 PolarDB 系列,例如 Polar-X , 其本身就支持范围和Hash分区,还支持动态扩展。
@ 水平扩展 · PolarDB-X 产品文档 (polardbx.com)[8]
这里就不细说了,文档里面比较完善。
总结
分库分表大概就这么多东西,到了这一步基础准备应该就差不多了,后续就是业务功能上的处理了。
这一篇主要是聊思想,对一些具体的业务没有体现,后续碰到有意思的会一起分析下。