1、分库分表的分类
- 垂直划分——分库,根据业务类型将不同的表,分到不同的数据库,主要用来解决单点数据库节点的压力瓶颈问题
- 水平划分——单数据库节点的分表,通过不同的表名来存储部分数据,用来解决单表数据量太大的问题。
- 水平划分——借用中间件,水平复制相同的数据库定义到多个实例节点,由中间件来实现分片逻辑
2、分片规则以及实现方式
分片之后,原生的SQL操作语义总是会弱化的,但是可喜的是,不同的业务场景下,对原生的SQL操作的语义弱化是能接受的,因此损失部分功能语义而提出的多样化分片规则,分别适合不同的业务场景,需要我们深入理解然后才能做到根据不同的业务场景进行合适的选择。
2.1、分片规则制订思考
- 分片数据如何插入。把数据识别出来,然后执行一下分片函数即可定位到节点,再执行对应的SQL即可。
- 分片数据如何关联查询。最难也是主要思考的地方,因为数据分布不同的节点,要全表关联是不容易实现的。
- 分片数据删除。把查询条件识别出来,然后执行一下分片函数即可定位到节点,再执行对应的SQL即可,对了提高执行效率,尽量避免join操作。
- 分片数据修改。把查询条件识别出来,然后执行一下分片函数即可定位到节点,再执行对应的SQL即可,对了提高执行效率,尽量避免join操作。
2.2、分片规则
- Mycat全局表 对于一些数据量不是特别大,insert/update操作又比较少的表,直接选择在所有的节点存储一份相同的数据。join操作是比较方便的。
- ER分片表 对于有强关联需求的主从表,分片之后,将会关联的数据放在一个节点,从而实现方便的join操作。
- 多对多关联 对于订单表来说,可能会基于商户会员查询,也可能会基于商户查询,但同时基于二者的分片是做不到的,所以只能基于业务场景选择是更倾向于哪一个关系,被选中的关系只需要查询单节点,未选择的关系查询需要查询所有相关的节点然后合成。
2.3、分片字段选取条件思考
- 尽可能将数据均匀的分布到各个节点
- 该业务字段是最频繁的或者最重要的查询条件
- 尽量避免跨库join操作是最重要的一条原则
2.4、分片规则算法配置
- 分片枚举。枚举出所有的节点,分片函数必须返回枚举的值,否则就会报错。
- 固定分片HASH算法。 类似于十进制的取模运算,但又不同,是取二进制的前10位,然后根据配置来运算得到节点。count:2,1 length:256,521,就是指前两个节点256/1024=25%,后一个节点521/1024=50%。这样的配置是把不同的区段值映射到不同的库节点中。相比十进制取模分片,事务处理相对比较容易,因为同一分段里的数据往往存储在一个节点,需要协调的事务节点较少。
- 范围约定。在配置文件中指定不同范围的值,存储到不同的节点。需要预先规划数据存储,适合范围一定,并且不同范围内值比较好预测的情况。
- 取模。比如有4个节点,直接对4取模,求值是多少,就存储到哪个节点。但是因为同一批插入或者更新的数据,因为id在不同的节点依次分布,事务协调难度增大很多。
- 按日期(天)。以指定日期为起点,每N天放入一个节点,到达结束日期,又循环这个过程。
- 取模范围约束。是取模和范围约定的结合,只是先取模,再根据范围约定,放到不同的节点。
- 截取数字做hash求模范围约束。与取模范围约束类似,此规则支持数据符号字母取模。取prefixLenght位列所有ASCII码的和进行求模。
- 应用决定。由应用自主决定路由到哪个分片。比如, id=05-100000002,可以配置成前两位数字,即05就是要路由的分片。
- 截取数字hash解析截取字符串中的int数值hash分片
- 一致性hash解释暂缺
- 按单月小时拆分单月内按照小时拆分,最小粒度是小时,可以一天最多24个分片,最少一个分片,一个月后下月从头开始循环,每个月月尾,需要手工清理数据。
- 范围求模分片先进行范围分片计算得出分片组,然后组内再求模以确认最终插入的节点。
- 日期范围hash分片思想与范围求模一致,当由于日期在取模时会有数据集中的问题,所以改成hash方法。
- 冷热数据分片
- 自然月分片
3、分别如何实现常见的SQL语句
要将单个数据库操作的SQL语句实现到数据库集群,每个操作都是需要深入思考实现的,为了实现类似的功能,必然会损失或者弱化部分SQL的原先语义或者效率。
join操作
这是实现起来最不容易的部分,跟分片规则有非常直接的关系,或者干脆说,考虑分片的时候,主要就是在考虑如何实现关联查询操作,其它操作实现起来都是比较简单的。只要分片规则一确定,并且没有join操作,插入/删除/更新就可以非常容易定位到相关节点,然后执行操作。
- 全局表 稳定不经常变的数据,在每个需要的数据节点都存有数据
- ER join join操作上可能存在关系的数据,存在一个数据节点上
- Share join 目前只支持2个表的Join操作,没有很理解它的原理:拆分成单表的SQL语句执行,然后把各个节点的数据汇总
- catlet(人工智能) 把所有表数据查询出来,然后在mycat这一层进行Join操作
- 未来扩展用spark/storm来进行Join操作
4、全局序列号
4.1、为什么要有全局序列号
在实现分库的情况下,数据库自增主键已无法保证自增主键的全局唯一。
4.2、如何实现?
- 本地文件方式 配置在本地文件中,获取完之后去更新最新值
- 数据库方式 与上面类似,只是存储媒介换成了数据库而以
- 本地时间戳方式 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加)
- 分布式ZK id生成器
- zk递增方式
- 自增长主键 Mycat自增长主键
5、高可用实现
5.1、实现思路探究
从Mycat作为一个无状态的数据库中间件定位来看,高可用只要考虑两个点:
- MYSQL节点本身的高可用;这个可以参考另一篇转载的博文五大常见的MySQL高可用方案
- Mycat自身的高可用;可以使用的LVS/HAPROXY或者专门的负载均衡的硬件,通过部署多个Mycat节点来实现高可用。如果担心负载均衡本身的高可用,可以使用keepalive来组合使用。参考如下图片
5.2、故障之后又是如何恢复的呢?
如果出现了异常,就先把机器摘下来,等手动恢复正常,追上master数据之后,就可以再以只读节点加入体系。
6、事务实现原理
单实例的事务处理在这里肯定没法直接有用了,这个时候必须要用到分布式提交协议(两阶段协议/三阶段协议/Paxos协议/Zookeeper)
我以为Mycat提供了几个XA实现,没想到还是挺弱的。如果有分片出错,Mycat可以保证全部能够回滚成功,因为就是超时了,也可以保证不会提交成功的。但是却没有办法保证全部commit成功。