1.分库分表方案
针对MySQL的分库分表,可以从不同层次切入。常见的切入层有:
1.1 编码层(不推荐)
在代码层面动态切换数据源,例如Spring中的AbstractRoutingDataSource,缺点也是显而易见的,需要编写大量的代码,照顾到每个分支。当涉及跨库查询、聚合,需要循环计算结果并合并的场景,工作量巨大。不建议使用该种方案。
1.2 框架层(不推荐)
这种情况适合公司ORM框架统一的情况,修改或增强现有ORM框架的功能,在SQL中增加一些自定义原语或者hint来实现。主要做法是通过实现一些拦截器(比如Mybatis的Interceptor接口),增加一些自定义解析来控制数据的流向,效果虽然较好,但会改变一些现有的编程经验。很多情况要修改框架源码,不推荐。
1.3 驱动层(ShardingJDBC)
基于在编码层和框架层切入的各种缺点,真正的数据库中间件起码要从驱动层开始。重新编写了一个JDBC的驱动,在内存中维护一个路由列表,然后将请求转发到真正的数据库连接中。
常用的方案为:TDDL、ShardingJDBC等。
Mysql Connector/J的Failover协议 (具体指“load balancing”、“replication”、“farbic”等), 也是直接在驱动上进行修改。

1.4 代理层(MyCat)
代理层的数据库中间件,将自己伪装成一个数据库,接受业务端的链接。然后负载业务端的请求,解析或者转发到真正的数据库中。
常用的方案为:MySQL Router、MyCat等

1.5 实现层(Mysql-Cluster,Galera-Cluster)
SQL特殊版本支持,如Mysql-Cluster本身就支持各种特性,Mariadb-Galera-Cluster支持对等双主,Greenplum支持分片等。
1.6 驱动层与代理层比较
1.6.1 驱动层的特点
1.仅支持JAVA,支持丰富的DB

2.占用较多的数据库连接
驱动层中间件要维护很多数据库连接。比如一个分了10个 库 的表,每个java中的Connection要维护10个数据库连接。如果项目过多,则会出现连接爆炸。像Postgres这种每个连接对应一个进程的数据库,压力会很大。
3.数据聚合在业务实例执行
数据聚合,比如count sum等,是通过多次查询,然后在业务实例的内存中进行聚合。路由表存在于业务方实例内存中,通过轮询或者被动通知的途径更新路由表即可。
4.集中式管理
所有集群的配置管理都集中在一个地方,运维负担小,DBA即可完成相关操作。
驱动层的典型实现

1.6.2 代理层的特点
1.异构支持,DB支持有限
与驱动层相反,代理层中间件仅支持一种后端关系型数据库,但支持多种开发语言。如果你的系统是异构的,并且都有同样的SLA要求,则推荐使用此方案。

2.运维负担大
代理层需要维护数据库连接数量有限(MySQL Router那种粘性连接除外)。但作为一个独立的服务,既要考虑单独部署,又要考虑高可用,会增加很多额外节点,更别提用了影子节点的公司了。
另外,代理层是请求唯一的入口,稳定性要求极高,一旦有高耗内存的聚合查询把节点搞崩溃了,都是灾难性的事故。
代理层典型实现

1.7 使用限制
1.确保数据均衡
拆分数据库的数据尽量均匀,比如按省份分user库不均匀,按userid取模会比较均匀。
2.不能深分页
不带切分键的深分页,会取出所有库所取页数之前的所有数据在内存排序计算。容易造成内存溢出。
3.减少子查询
子查询会造成SQL解析紊乱,解析错误的情况,尽量减少SQL的子查询。
4.事务最小原则
尽量缩小单机事务涉及的库范围,即尽可能减少夸库操作,将同类操作的库/表分在一起。
5.数据均衡原则
拆分数据库的数据尽量均匀,比如按省份分user库不均匀,按userid取模会比较均匀。
6.特殊函数
distinct、having、union、in、or等,一般不被支持。或者被支持,使用之后会增加风险,需要改造。
2.分库分表中间件
2.1 活跃中的项目
2.1.1 [活跃]-ProxySQL(轻量级)
ProxySQL是一个可以实现MySQL读写分离的轻量级工具。ProxySQL的特点:将所有配置保存写入到SQLit表中。支持动态加载配置,即一般可以在线修改配置,但有少部分参数还是需要重启来生效。支持query cache。支持对query的路由,可以针对某个语句进行分配去哪个实例执行。故障切换。过滤危险的SQL。不支持分表,可以分库,但是利用规则配置实现分表。

MHA+ProxySQL实现读写分离高可用:https://www.cnblogs.com/gomysql/p/7018797.html
项目地址: https://github.com/sysown/proxysql
2.1.2 [活跃]-Ctrip DAL(携程)
Ctrip DAL是携程框架部开发的数据库访问框架,支持代码生成和水平扩展。其由携程技术中心框架部DAL团队开发,历经3年不断打磨,并在长期的实际使用中基于大量的用户反馈不断优化。
Ctrip DAL支持流行的分库分表操作,支持Java和C#,支持Mysql和MS SqlServer。使用该框架可以在有效地保护企业已有数据库投资的同时,迅速,可靠的为企业提供数据库访问层的横向扩展能力。整个框架包括代码生成器和客户端。工作模式是使用代码生成器在线生成代码和配置,通过DAL客户端完成数据库操作。生成器具有丰富的向导指引,操作简单清晰,即可以批量生成标准DAO,也可以在方法级别定制数据库访问。

项目地址: https://github.com/ctripcorp/dal
2.1.3 [活跃]-Vitess(Youtube)
这个中间件是Youtube生产在使用的,但是架构很复杂。 与以往中间件不同,使用Vitess应用改动比较大要 使用他提供语言的API接口,我们可以借鉴他其中的一些设计思想。Vitess 是一个用于 MySql 扩展的数据库解决方案。它以能够像运行在专用硬件上那样有效地运行于云体系。它集 MySql 数据库的很多重要特性和 NoSQL 数据库的可扩展性于一体。Vitess 已经成功侍服了 2011 年以来所有的 YouTube 数据库流量。
理解VITESS: https://www.cnblogs.com/zhangwushang/p/8523015.html
项目地址:https://github.com/vitessio/vitess
2.1.4 [活跃]-Heisenberg(Cobar增强版)
heisenberg 是百度的熊照同学(id:brucexx)编写的一款基于MySQL协议之上的分库分表中间件服务器,支持各种灵活(velocity脚本自定义)的分库分表规则,做到应用和分库分表相隔离,并且为mysql进行dbproxy,减少了db的连接IO压力,并且可做到读写分离以及replication的手工切换。
改编自cobar, 结合了cobar和TDDL的优势,让其分片策略变为分库表策略,节约了大量连接,其优点: 分库分表与应用脱离,分库表如同使用单库表一样 减少db 连接数压力 热重启配置 可水平扩容 遵守Mysql原生协议 读写分离 无语言限制,mysqlclient,c,java等都可以使用 Heisenberg服务器通过管理命令可以查看,如连接数,线程池,结点等,并可以调整 采用velocity的分库分表脚本进行自定义分库表,相当的灵活。

项目地址: https://github.com/brucexx/heisenberg
2.1.5 [活跃]-Mycat(Cobar增强版)
2013 年阿里的 Cobar 在社区使用过程中发现存在一些比较严重的问题,及其使用限制,经过 Mycat 发起人第一次改良,第一代改良版——Mycat 诞生。 Mycat 开源以后,一些 Cobar 的用户参与了 Mycat 的开发,最终 Mycat 发展成为一个由众多软件公司的实力派架构师和资深开发人员维护的社区型开源软件。

项目地址: https://github.com/MyCATApache/Mycat-Server
利用MyCAT实现MySQL的读写分离和主从切换:https://blog.csdn.net/leonpenn/article/details/77278360
项目地址:https://github.com/MyCATApache/Mycat-Server
2.1.6 [活跃]-Sharding-JDBC(当当)
Sharding-JDBC是当当应用框架ddframe中,从关系型数据库模块dd-rdb中分离出来的数据库水平分片框架,是继dubbox、elastic-job之后ddframe开源的第三个项目。Sharding-JDBC直接分装jdbc协议,可理解为增强版的JDBC驱动,旧代码迁移成本几乎为零,定位为轻量级java框架,使用客户端直连数据库,以jar包形式提供服务,无proxy层。

项目地址: https://github.com/sharding-sphere/sharding-sphere
2.2 停滞或未开源的项目
2.2.1 [停滞]-kingshard(Go语言)
一个由Go开发高性能MySQL Proxy项目,kingshard在满足基本的读写分离的功能上,致力于简化MySQL分库分表操作,能够让DBA通过kingshard轻松平滑地实现MySQL数据库扩容。
sharding方式:range方式、hash方式
分表方案采用两级映射的方式:
1.kingshard将该表分成512张子表,例如:test_0000,test_0001,…test_511。
2.将shardKey通过hash或range方式定位到其要操作的记录在哪张子表上。
3.子表落在哪个node上通过配置文件设置。
基于kingshard的子表迁移方案:
1.通过自动数据迁移工具开始数据迁移。
2.数据差异小于某一临界值,阻塞老子表写操作(read-only)
3.等待新子表数据同步完毕
4.更改kingshard配置文件中的对应子表的路由规则。
5.删除老节点上的子表。
参考网址: https://segmentfault.com/a/1190000003001545
项目地址: https://github.com/flike/kingshard
2.2.2 [停滞]-MySQL Fabric(官方)
为了实现和方便管理MySQL 分片以及实现高可用部署,Oracle在2014年5月推出了一套为各方寄予厚望的MySQL产品 – MySQL Fabric, 用来管理MySQL 服务,提供扩展性和容易使用的系统,Fabric当前实现了两个特性:高可用和使用数据分片实现可扩展性和负载均衡,这两个特性能单独使用或结合使用。

MySQLFabric概述: https://www.cnblogs.com/huaxingtianxia/p/7095193.html
MySQL原生HA方案 – Fabric体验之旅: https://www.csdn.net/article/2014-08-20/2821300
官网: https://downloads.mysql.com/archives/utilities/
2.2.3 [停滞]-Oceanus(58同城)
58同城数据库中间件。Oceanus致力于打造一个功能简单、可依赖、易于上手、易于扩展、易于集成的解决方案,甚至是平台化系统。拥抱开源,提供各类插件机制集成其他开源项目,新手可以在几分钟内上手编程,分库分表逻辑不再与业务紧密耦合,扩容有标准模式,减少意外错误的发生。最后更新时间2015年。
项目地址:https://github.com/58code/Oceanus

2.2.4 [停滞]-TSharding(蘑菇街)
TSharding 是应用于蘑菇街交易平台的一个简易 sharding 组件,也是一个 Mybatis 分库分表组件。

项目地址: https://github.com/baihui212/tsharding
2.2.5 [未开源]-DDB(网易)
DDB(Distributed database)是网易杭研院立项最早,应用最为广泛的后台产品之一,也是国内最早出现的基于现有database之上开发的分布式数据库中间件,目前依然在为网易易信,云音乐,云阅读等大型互联网产品提供稳定的数据库服务。
网易分库分表数据库DDB:https://mp.weixin.qq.com/s/D03pO_wwPyjKpSK6BQIoJg
2.2.6 [停滞]-TDDL(阿里)
淘宝根据自身业务需求研发了TDDL(Taobao Distributed Data Layer)框架,主要用于解决分库分表场景下的访问路由(持久层与数据访问层的配合)以及异构数据库之间的数据同步,它是一个基于集中式配置的JDBC DataSource实现,具有分库分表、Master/Salve、动态数据源配置等功能。就目前而言,许多大厂也在出一些更加优秀和社区支持更广泛的DAL层产品,比如Hibernate Shards、Ibatis-Sharding等。项目最后一次更新为2012年。
TDDL:来自淘宝的分布式数据层:https://blog.csdn.net/diu_brother/article/details/51554555
官网: https://github.com/alibaba/tb_tddl
2.2.7 [未开源]-MTDDL(美团点评)
MTDDL(Meituan Distributed Data Layer),美团点评分布式数据访问层中间件,旨在为全公司提供一个通用数据访问层服务,支持MySQL动态数据源、读写分离、分布式唯一主键生成器、分库分表、动态化配置等功能,并且支持从客户端角度对数据源的各方面(比如连接池、SQL等)进行监控,后续考虑支持NoSQL、Cache等多种数据源。
MTDDL——美团点评分布式数据访问层中间件:https://tech.meituan.com/mtddl.html
2.2.8 [停滞]-Zebra(美团点评)
Zebra是美团点评内部使用的数据库访问层中间件,它具有以下的功能点:配置集中管理,动态刷新。支持读写分离、分库分表。丰富的监控信息在CAT上展现
项目地址: https://github.com/Meituan-Dianping/Zebra
2.2.9 [停滞]-Cobar(阿里)
Cobar是提供分布式数据库服务的中间件,由阿里中间件团队开发,是阿里巴巴B2B前台应用访问数据库的统一入口,Cobar的分布式方案是分库和分表,可以按照业务需求将数据库中耦合度较低的表分到不同的分库中,也可以按照具体表的增长速度和数据量水平切分到不同的分库中,Cobar可以实现应用层与物理分库的双向透明,从而实现应用程序访问分布式数据库与访问单库无差别。 Cobar还可以配合MySQL的心跳和binlog实现备机的自动切换,保证数据节点的可靠性,从而实现高可用性。

项目地址: https://github.com/alibaba/cobar
2.2.10 [未开源]-OneProxy(代理层)
OneProxy是由原支付宝首席架构师楼方鑫开发,目前由楼方鑫创立的杭州平民软件公司(@平民架构)提供技术支持。目前已有多家公司在生成环境中使用,其中包括了支付、电商等行业。

OneProxy的主要功能有:1. 垂直分库 2. 水平分表 3. Proxy集群 4. 读高可用 5. 读写分离(master不参与读)6. 读写分离(master参与读)7. 写高可用 8. 读写随机
OneProxy使用手册-致力于打造透明的数据层: https://www.cnblogs.com/youge-OneSQL/articles/4208583.html
2.3 其他数据库项目
2.3.1 [活跃]-Greenplum(PostgreSQL)
GreenPlum建立在PostgreSQL的基础上,把许许多多的PostgreSQL节点组织在一起,实现了一个强大的分布式数据库。集群包括两类角色:master和segment。
GreenPlum数据库默认会采用Hash Distribution:如果创建表时没有指定Distribution Key,则会选择Primary Key作为Distribution Key。如果Primary Key也不存在,就会选择表的第一列作为Distribution Key。和大多数的分布式系统一样,GreenPlum也支持垂直和水平两种扩容方式,垂直扩容又被称为presharding模式。

【GreenPlum】GreenPlum服务来了!: https://sq.163yun.com/blog/article/172464991000825856
2.3.2 [活跃]-MariaDB Spider(Mariadb)
MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。在存储引擎方面,使用XtraDB(英语:XtraDB)来代替MySQL的InnoDB。 MariaDB由MySQL的创始人Michael Widenius(英语:Michael Widenius)主导开发,他早前曾以10亿美元的价格,将自己创建的公司MySQL AB卖给了SUN,此后,随着SUN被甲骨文收购,MySQL的所有权也落入Oracle的手中。MariaDB名称来自Michael Widenius的女儿Maria的名字。
MariaDB基于事务的Maria存储引擎,替换了MySQL的MyISAM存储引擎,它使用了Percona的 XtraDB,InnoDB的变体,分支的开发者希望提供访问即将到来的MySQL 5.4 InnoDB性能。这个版本还包括了 PrimeBase XT (PBXT) 和 FederatedX存储引擎。
Spider是MariaDB内置的一个可插拔用于MariaDB/MySQL数据库分片的存储引擎,充当应用服务器和远程后端DB之间的代理(中间件),它可以轻松实现MySQL的横向和纵向扩展,突破单台MySQL的限制,支持范围分区、列表分区、哈希分区,支持XA分布式事务,支持跨库join。通过Spider,您可以跨多个数据库后端有效访问数据,让您的应用程序一行代码不改,即可轻松实现分库分表!

MariaDB Spider:实现MySQL横纵向扩展的小能手: https://www.sohu.com/a/135228509_487514
官网: https://mariadb.com/kb/en/library/spider/
3.扩容方案如何做?
3.1 水平分库扩展的问题
水平分库常用的分库策略是采用取模的方式,对userId或业务ID,当业务快速的发展,用户量数据大量上升,当前容量不足以支撑,就需要对数据库进行水平扩容,再增加新库来分解。新库加入之后,原先sharding到3个库的数据,就可以sharding到4个库里面了。
不过此时由于分片规则进行了变化(uid%3 变为uid%4),大部分的数据,无法命中在原有的数据库上了,需要重新分配,大量数据需要迁移。比如之前uid1通过uid1%3 分配在A库上,新加入库D之后,算法改为uid1%4 了,此时有可能就分配在B库上面了。如果你了解过一致性哈希的原理,就会发现新增一个节点,大概会有90%的数据需要迁移,这个对DB同学的压力还是蛮大的,那么如何应对?
3.2 方案一:停服迁移
停服迁移是最常见的一种方案了,一般如下流程:1.预估停服时间,发布停服公告。2.停服,通过事先做好的数据迁移工具,按照新的分片规则,进行迁移。3.修改分片规则。4.启动服务。
缺点:
1.停服,伤害用户体验,同时也降低了服务器的可用性
2.必须在制定时间内完成迁移,如果失败,需要择日再次进行。同时增加了开发人员的压力,容易发生大的事故
3.数据量的巨大的时候,迁移需要大量时间
3.3 方案二:升级从库
线上数据库,我们为了保持其高可用,一般都会每台主库配一台从库,读写在主库,然后主从同步到从库。
升级从库方案步骤:
1.修改分片配置,做好新库和老库的映射。
2.同步配置,从库升级为主库
3.解除主从关系
4.冗余数据清理
5.为新的数据节点搭建新的从库
示意图如下:



3.4 方案三:双写迁移
原理和上述相同,做分裂扩容,只是数据的同步方式不同了
双写迁移方案步骤:
1.增加新库写链接
双写的核心原理,就是对需要扩容的数据库上,增加新库,并对现有的分片上增加写链接,同时写两份数据。因为新库的数据为空,所以数据的CRUD对其没有影响,在上层的逻辑层,还是以老库的数据为主。
2.新老库数据迁移
通过工具,把老库的数据迁移到新库里面,此时可以选择同步分裂后的数据(1/2)来同步,也可以全同步,一般建议全同步,最终做数据校检的时候好处理。
3.数据校检
按照理想环境情况下,数据迁移之后,因为是双写操作,所以两边的数据是一致的,特别是insert和update,一致性情况很高。但真实环境中会有网络延迟等情况,对于delete情况并不是很理想,此时就需要做好数据校检了,数据校检可以多做几遍,直到数据几乎一致,尽量以旧库的数据为准。
4.分片配置修改
数据同步完毕,就可以把新库的分片映射重新处理了,还是按照老库分裂的方式来进行,u之前uid%2=0,变为uid%4=0和uid%4=2的。uid%2=1,变为uid%4=1和uid%4=3的。
示意图如下:



4.参考资料
分库分表?选型和流程要慎重,否则会失控:https://juejin.im/post/5bf778ef5188251b8a26ed8b
MySQL分库分表方案: https://www.cnblogs.com/sunny3096/p/8595058.html
分布式数据中间件TDDL、Amoeba、Cobar、MyCAT架构比较: https://blog.csdn.net/kobejayandy/article/details/60869530
假如让你来设计数据库中间件: https://mp.weixin.qq.com/s/6kuVgdO7RBs9gs229wG3wA
数据库中间件Atlas调研笔记: https://mp.weixin.qq.com/s/31WOensXaLdaAp9WRMW7PA
数据库中间件TDDL调研笔记: https://mp.weixin.qq.com/s/dVGSvUR9UCA-dIYVtr_G_w
数据库中间件cobar调研笔记: https://mp.weixin.qq.com/s/nfTKSTpCvNcvNFAdl2J7mQ
mysql-proxy数据库中间件架构: https://mp.weixin.qq.com/s/Ozqu2A7Sy_TGKkF6yF1rDQ
水平分库如何做到平滑扩展: https://mp.weixin.qq.com/s/mOh_PCVW68JCVSd19CGZ0A
mysql中间件研究(Atlas,Cobar,TDDL): https://www.guokr.com/blog/475765/
mycat分布式mysql中间件(mysql中间件研究): http://songwie.com/articlelist/44