MySQL分库分表详解
摘要:本文是MySQL第七讲:MySQL分库分表详解流程,非常全面的总结,强烈建议保存下来,在需要时看看
文章目录
- MySQL分库分表详解
-
-
- 1、什么是分库分表,何时要分库分表
-
- 背景知识:
- 1.1、数据库分片
- 1.2、为什么需要分库分表
- 1.3、拆分方式
- 1.4、何时分库分表
- 1.5、如何选择分库分表
- 2、十亿级商品数据,分库分表核心流程详解
-
- 2.1、完整流程如下所述
- 2.2、使用到的分库分表工具
- 3、分库分表带来的问题及如何解决(重点)
-
- 问题1:分布式id:分库分表后,保证id的唯一性(要保证单调递增)
- 问题2、分布式事务问题
- 问题3、跨库join / 分页查询问题
- 问题4、成本问题
- 4、日千万级的订单系统分库分表核心内容简要分享
- 5、阿里云drds方案处理分库分表
- 6、MySql分库分表与读写分离
-
- 6.1、什么是MyCat?
- 6.2、中间件操作数据库的步骤
- 6.3、MyCat使用案例
- 6.4、MyCat关联查询的问题
- 6.5、Mycat的分布式事务?XA ?20181222
-
1、什么是分库分表,何时要分库分表
背景知识: 1、海量数据的存储问题
- 传统的关系性数据库已经无法满足快速查询与插入数据的需求。这个时候NoSQL的出现暂时解决了这一危机。它通过降低数据的安全性,减少对事务的支持,减少对复杂查询的支持,来获取性能上的提升。有些场合NoSQL是无法满足使用场景的,比如有事务与安全指标的。从单机mysql --》memcached+mysql+垂直拆分 --》mysql主从复制 --》分库分表+水平拆分+mysql集群
- 此时就需要做数据库集群,为了提高查询性能将一个数据库的数据分散到不同的数据库中存储。
- 数据库连接数,默认是100个连接数
- 单表数据量大,阿里规定单表数据量500万条 数据量大,IO操作就多
- 硬件资源(QPS/TPS 每秒查询数/每秒事务量)
- 分库分表
- 索引优化(数据量1个亿且查询不太复杂时,没什么问题)
- 读写分离
- 1、什么是数据库分片
指通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库上面,以达到分散单台设备负载的效果
- 分库,就是把数据拆分到不同的 MySQL 库中去
- 如下图所示,库里面放了两张表:SPU和SKU表,我们将这两张表拆分到两个不同的库里面
文章图片
- 如下图所示,库里面放了两张表:SPU和SKU表,我们将这两张表拆分到两个不同的库里面
- 分表:在数据量不变的情况下,把数据拆分到同一个库的多张表里面
- 如下图所示,将SPU表由一张拆分为四张
文章图片
- 如下图所示,将SPU表由一张拆分为四张
- 分库分表:数据库数量和表数量都发生变更
- 如下图所示,库里面放了一张SPU表,我们将这张表拆分到两个不同的库里面,每个库拆分成两张表
文章图片
- 如下图所示,库里面放了一张SPU表,我们将这张表拆分到两个不同的库里面,每个库拆分成两张表
- 使用场景
- 绝大部分的电商大厂,只有 MySQL 这类关系型数据库,才能提供金融级的事务保证
- MySQL 支持不了这么大的数据量,这么高的并发,但还必须要用它
- ①数据量太大查询慢(事务中的查询和更新操作)
- 只读的查询可以通过缓存和主从分离来解决
- 解决查询慢,只要减少每次查询的数据总量就可以了,也就是说,分表就可以解决问题
- ②应对高并发的问题
- 一个数据库实例撑不住,就把并发请求分散到多个实例中去
- 数据量大,就分表;并发高,就分库
- 这时候分多少个库,多少张表,分别用预估的并发量和数据量来计算就可以
- 越简单的设计可靠性越高
- ①垂直切分(复杂度低):即将表按照功能模块、关系密切程度划分出来,部署到不同的库上。我们会建立定义数据库 workDB、商品数据库 payDB、用户数据库 userDB、日志数据库 logDB 等,分别用于存储项目数据定义表、商品定义表、用户数据表、日志数据表等
垂直拆分带来的问题?
分布式事务(通过模块调用接口来解决)
–》从连接数和硬件资源两方面进行了解决 - ②水平切分(复杂度高):当一个表中的数据量过大(超过1000万,就要警觉起来)时,我们可以把该表的数据按照某种规则,例如userID散列,进行划分,然后存储到多个结构相同的表,和不同的库上。
文章图片
垂直拆分如图所示:
文章图片
水平拆分如图所示:
文章图片
- 总体原则:那就是能不拆就不拆,能少拆不多拆,先才用加索引、读写分离解决性能问题,实在不行后,才考虑使用分库分表
- 读写分离(主从模式)
- 1、什么是读写分离
- 在实际的应用中,绝大部分情况都是读远大于写,Mysql提供了读写分离的机制,所有的写操作都必须对应到Master,读操作可以在Master和Slave机器上进行,Slave与Master的结构完全一样,甚至Slave下还可以挂Slave,通过此方式可以有效的提高DB集群的每秒查询率。 一个写节点Master后面跟着多个读节点,所有的写操作都是先在Master上操作,然后同步更新到Slave上
- 2、MySql基于binlog的主从复制原理(通过数据库厂商提供的bin log机制)
- master–》binary log《-》io Thread–write–》relay log(中继日志)《–》sql Thread
- 复制的基本原理?
- 1、binlog事件 //master将改变记录到二进制文件binary log;
- 2、relay log //slave将master的binlog事件拷贝到他的中继日志
- 3、slave重做中继日志中的事件,将改变应用到自己的数据库中,Mysql复制是异步的且串行化
- 3、主从复制带来的问题?
- 可能出现时延,使用级联主从来解决,或是代码判断,要是真的出现时延,再进行解决;
- 判断主从是否出现延时seconds-behind-master:0;
- 若是超过5秒,则强制去主库读取数据
- =>一般的业务不可能把主从搞出延时,老师只遇见过一次:到同行数据时,600万用户数据同时加载
- =》因为数据库之间是在内网传输,延时一般很小
- 4、总结:
- 1、当读压力很大的时候,可以考虑添加Slave机器的分式解决,但是当Slave机器达到一定的数量就得考虑分库了;
- 2、当写压力很大的时候,就必须得进行分库操作;
- 3、全局的ID(不能使用mysql的自增id,要使用全局的id)
- 1、什么是读写分离
- 单表出现瓶颈:
- 单表数据量较大,导致读写性能较慢
- 单库出现瓶颈
- CPU压力过大(busy,load过高),导致读写性能较慢
- 内存不足(缓存池命中率较低、磁盘读写IOPS过高),导致读写性能较慢
- 磁盘空间不足,导致无法正常写入数据
- 网络带宽不足,导致读写性能较慢
Action : 数据量超过多少应该分库分表:看对应业务复杂情况,如果是表字段较为简单,即使数据量超过亿级,整体读写性能也较好,不用分表;如果表比较复杂,可能即使数据量超过百万,读写性能就达到瓶颈。1.5、如何选择分库分表
- 只分表:
- 单表数据量较大,单表读写性能出现瓶颈
- 经过评估单库的容量和性能可以支撑未来几年的增长
- 只分库:
- 数据库(读)写压力较大,数据库出现存储性能瓶颈
- 分库分表
- 单表数据量较大,单表读写性能出现瓶颈;
- 数据库(读)写压力较大,数据库出现存储性能瓶颈
2.1、完整流程如下所述
- 评估是否需要拆分
- 是否有其他更轻量的优化手段可以解决问题
- 拆分详细技术方案
- 流程梳理及影响评估
- 方案选型
- 拆分SOP(标准操作程序,重要)
- 1、目标评估
- 评估:拆成几个库、几个表
- 目标:读写能力提升X倍、负载降低Y%,容量要支持未来Z年的发展
- 举例:当前2亿,5年后评估为10亿。分几个表? 分几个库?
- 解答:一个合理的答案,128个表,16个库。按128个表算,拆分完单表156万,5年后为781万
- 2、切分策略–范围切分
- 优点:天然水平扩展;单表大小可控
- 缺点:热点数据一般为新增数据,存在明显的写偏移
- 适用场景:数据归档
文章图片
- 切分策略–中间表映射
- 优点:灵活;
- 缺点:引入了额外的单点,增加了流程复杂度。
文章图片
- 切分策略–hash切分
- 优点:数据分片比较均匀,不容易出现热点和并发访问的瓶颈;
- 缺点:后续扩容需要迁移数据、存在跨节点查询等问题;
- 适用场景:大部分场景下都能适用。
文章图片
- 3、分表字段(sharding key)
- 核心思路:合理选择,尽量减少出现跨库、跨表查询
- 例子:10个库,1000张表:0 ~ 99、100 ~ 199、200~999、…
- 分表字段:shopId,值为1234
- 数据表编号:shopId% 1000 =1234 % 1000 =234
- 数据库编号:shopId% 1000 =1234 % 1000 / 10 = 2
- 思考:订单应该如何选择分表字段? 见下文
- 4、资源准备、代码改造
- 核心流程:
- 数据库资源准备
- 分库分表规则配置等
- 代码改造:
- 写入:单写老库、双写、单写新库
- 读取:读老、读新、部分读老部分读新
- 灰度:指定门店灰度、比例灰度
- 所需资源尽早跟dba申请
- 1、将新集群的数据源引入到我们的服务中;
- 2、支持灵活的灰度读写切换;
- 3、数据全量迁移和一致性校验等任务;
- 4、增量数据同步:双写
- 核心流程:
- 5、常见的双写方案
- 作用:保证增量数据在新库和老库都存在
- 方案:
- 1、同步双写:同步写新库和老库
- 2、异步双写:写老库,监听binlog 异步同步到新表
- 3、中间件同步工具:通过一定的规则将数据同步到目标库表
- 我们公司使用的是阿里云DTS
- 注意点:
- 写新库异常不能影响流程
- 实现方案:
- 底层通过AOP方式实现,不会修改全部写逻辑
- 6、全量数据迁移
- 作用:迁移老库历史数据,保证新库有全量数据
- 方案:
- 1、开发Job:查询老库数据,写入新表;
- 2、中间件同步工具:通过一定的规则将数据同步到目标库表(建议)
- 注意点:
- 控制好同步速率
- 和增量数据的并发问题
- 7、数据一致性校验、优化、补偿 (最重要)
- 作用:确保新库数据正确,达到切读标准、检查是否存在改造遗漏点
- 方案:增量数据校验、全量数据校验、人工抽检
- 核心流程:
- 读取老库数据
- 读取新库数据
- 比较新老库数据,一致则继续比较下一条数据
- 不一致则进行补偿:
- 新库存在,老库不存在:新库删除数据
- 新库不存在,老库存在:新库增加数据
- 新库存在,老库不存在:比较所有字段,不一致则将新库更新为老库数据
- 8、灰度切读
- 作用:开始将读流量切到新库
- 原则:
- 有问题及时切回老库
- 灰度放量先慢后快,每次放量观察一段时间
- 支持灵活的规则:门店维度灰度、百分比灰度
- 9、databus 切新库
- 作用:使用新库的databus、canal
- 核心流程:
- 启动新库databus、canal,此时下游会同时收到新老库的binlog
- 观察一段时间是否正常
- 有问题及时关闭
- 没问题后,关闭老库databus、canal
- 10、下游切换数据源
- 作用:确保下游迁移到新数据源,主要是数仓
- 数仓一般是每天同步一次数据,因此在指定时间内切换即可。
- 11、停写老库
- 原则:确认老库数据源全部迁移后,停写老库
- 至此,核心拆分流程结束,后续逐步将老数据库资源逐渐下线。
- 整体流程图汇总如下:
-
文章图片
- 1、目标评估
- 稳定性保障
- 技术方案内部评审及优化
- 同步相关影响方
- 进行拆分
- binlog监听工具
- Databus
- Canal
- 分库分表工具
- 1、增强版的JDBC驱动
- 以客户端 jar包形式提供了对JDBC的封装,客户端直连数据库
- 开源:Sharding-JDBC、TDDL、Zebra
- 2、数据库代理
- 需要单独部署,客户端连接代理服务,代理服务负责跟数据库打交道
- 开源:Sharding-Proxy、MyCat
- 收费:阿里云DRDS
- 1、增强版的JDBC驱动
问题1:分布式id:分库分表后,保证id的唯一性(要保证单调递增)
- 解决方案1:UUID
- 优点:
- 本地生成,性能高
- 缺点:
- 更占用存储空间,一般为长度36的字符串
- 不适合作为MySQL主键
- 无序性会导致磁盘随机IO、叶分裂等问题
- 普通索引需要存储主键值,导致B+树“变高”,IO次数变多
- 基于MAC地址生成的算法可能导致MAC地址泄漏
- 优点:
- 方案2:雪花算法
-
文章图片
- 41 bit时间戳:可用69年
- 10bit工作机器:可部署1024台服务器
- 12bit序列号:每毫秒可生成4096个ID,每秒也就是409万
-
- 方案3:号段模式
-
文章图片
- 原来:是按数据表自增得到唯一id,现在:批量生成1000条数据,拿到其ids
-
- 用户视角:查询商品时,会展示shopId
- 商家视角:查询商品时,也会展示shopId
- shopId覆盖了最高频的几个使用场景
- 用户视角:查询到自己的所有订单,用户id
- 商家视角:商品查询自己的所有订单,商家id
- 运营视角:订单id字段
- 为了支持按订单号查询,可以把用户id的后几位放到订单号中
- 选择一个合适的列或者说是属性,作为分表的依据,这个属性一般称为 Sharding Key
- 原本在同一个数据库中不同的表可以在同一个事务中修改,业务分库后,表分散到不同的数据库中,无法通过事务统一修改。虽然数据库厂商提供了一些分布式事务的解决方案(例如,MySQL 的 XA),但性能实在太低,与高性能存储的目标是相违背的。
- 解决方案
- 1、2PC(Two Phase Commitment)
- 如下图所示,核心思想是将事务操作分为两个阶段,第一阶段是协调者首先询问所有事务参与者是否可以执行事务的提交操作;第二阶段是协调者根据所有参与者返回结果决定是否提交操作,如果全部的参与者都返回成功,则协调者向所有参与者发送事务提交请求;否则协调者向所有参与者发送事务中断回滚请求
- 优点:流程简单
- 缺点:存在同步阻塞、协调者单点等问题
- 数据库层面的处理
-
文章图片
- 方案2:TCC (try confirm cancel)
- 核心思想:针对每个操作都有一个对应的确定和取消操作 ,TCC中有主服务(可以理解为交易)和从服务(可以理解为订单和库存)两个角色。首先主服务会调用所有从服务的try接口进行业务检查和资源预留,然后主服务会根据所有从服务返回结果决定是否提交事务;如果从服务确认成功,则调用所有从服务的confirm接口进行事务确认提交操作,否则会调用所有从服务的cancel接口执行事务的取消,并释放预留资源;
- 应用层面的处理
文章图片
- 实际业务中的使用方式(保证最终一致即可,金融场景是个特例)
- 回滚
- 重试
- 监控
- 告警
- 幂等
- 对账
- 人工补偿(终极手段)
- 1、2PC(Two Phase Commitment)
- 业务分库后,原本在同一个数据库中的表分散到不同数据库中,导致无法使用 SQL 的 join
查询。
- join操作
水平分表后,数据分散在多个表中,如果需要与其他表进行 join 查询,需要在业务代码或
者数据库中间件中进行多次 join 查询,然后将结果合并。
- 【深入理解数据库|MySQL第七讲(MySQL分库分表详解)】count操作
水平分表后,虽然物理上数据分散到多个表中,但某些业务逻辑上还是会将这些表当作一个表来处理。例如,获取记录总数用于分页或者展示,水平分表前用一个 count() 就能完成的操作,在分表后就没那么简单了。常见的处理方式有下面两种:
- ①count() 相加:具体做法是在业务代码或者数据库中间件中对每个表进行 count() 操作,然后将结果相加。这种方式实现简单,缺点就是性能比较低。例如,水平分表后切分为 20 张表,则要进行 20 次 count(*) 操作,如果串行的话,可能需要几秒钟才能得到结果。
- ②记录数表:具体做法是新建一张表,假如表名为“记录数表”,包含 table_name、row_count 两个字段,每次插入或者删除子表数据成功后,都更新“记录数表”。
- order by 操作
水平分表后,数据分散到多个子表中,排序操作无法在数据库中完成,只能由业务代码或者数据库中间件分别查询每个子表中的数据,然后汇总进行排序。
- 方案1:选择合适的分表字段(sharding key)
- 合理选择,避免过多的跨库查询 ,保证绝大多数高频查询场景
-
文章图片
- 方案2:使用搜索引擎支持ES
- 数据冗余到ES,使用ES支持复杂查询
- 核心流程:
- 使用ES查询出关键字段,例如:店铺id和商品id
- 再使用关键字段去数据库查询完整数据
- 注意点
- ES只存储需要搜索的字段
- 方案3:分开查询,内存中聚合
- 先查询出A表数据,然后根据A表的结果查询B表
- 注意点:
- 查询出来的数据量
- 占用内存情况
- 方案4:冗余字段
- A表查询需要B表的field1字段,则将B表的field存储一份到A表上
- 适用场景:
- 只需要少量字段,则可以直接冗余
- 业务分库同时也带来了成本的代价,本来 1 台服务器搞定的事情,现在要 3 台,如果考虑
备份,那就是 2 台变成了 6 台。
todo
5、阿里云drds方案处理分库分表
- 商品中心的方案
- 背景:商品主表共1亿6千多万条数据(截止20210810,有效数据共1亿1千万条数据)
- 架构:使用阿里云提供的drds,分为三层,第一层是drds代理层(读写库sg85 只读库wr20),第二层是主rds数据库,共4个实例(16台机器),从rds数据库,共8个实例(32台机器)
项目的读操作,只会读取从rds数据库中的部分实例
- 用户中心的方案
- todo
6.1、什么是MyCat?
- 面向企业应用开发的“大数据集群”,支持事务、ACID、一个数据库中间件产品(作为后面MySql集群的proxy使用,默认端口:8066)
- 作用:提高可用数据分片集群,支持读写分离,支持MySql双主多从,一主(写数据库)多从(读数据库)
- 当添加一条记录时要向哪个数据库中插入呢?这些问题处理起来都是非常的麻烦。这种情况下可以使用一个数据库中间件mycat来解决相关的问题
- 用法:现在把数据库中所有的item一千五百万本(图书表)分片存储到三个数据节点中。可以是三台mysql数据库。
- 节点一:db1存储500万条数据 节点二:db2 500万;节点三:db3 500万;
- 如何分配:1、根据主键id按段切分 2、根据id取模运算
- 配置schema。xml文件,管理mycat的逻辑库,表,分片规则,数据节点,数据源 rule= auto-sharding-long mycat会根据此规则自动分片
- 读写分离:一个写节点master后面跟着多个读节点。读节点的数量取决于系统的压力。写数据库和读数据库建立主从复制,使用mysql厂商自带的binlog,然后建立mycat和mysql的心跳检查。
- 使用:mysql主数据库修改my.conf文件,musqld下面添加数据库及IP的配置信息 状态:File/position/binlog_Do_DB/binlog_ignore-DB。 从数据库修改my.conf文件 change master to master_host=‘’,master_post=‘’,master_user=‘’,master_password=‘‘master_log_file=’’,master_log_pos=‘’
当slave_io_Running:YES且slave_SQL_Running:YES,说明配置成功
mycat配置 支持mysql主从复制状态绑定的读写分离机制 balance writeType switchType
- 1、解析sql语句
- 2、数据源管理
- 3、数据源分配
- 4、请求/响应
- 5、结果整合
应用程序分为db_user(用户表、数据字典,用户地址表)和db_store(订单表,店员表)
经常使用的数据不变性,在后台的每个mysql中都保存一份(如数据字典最好冗余查询**)
用户表进行模2运算,store表进行主从复制
2、conf目录下,有三个关键的配置文件
- scheme.xml管理mycqt实例中的逻辑库,表,分片规则,DataNode,DataSource
- server.xml
- rule.xml 定义了我们队表进行拆分所设计到的规则定义
有连续分片和离散分片两种
连续分片扩容无需迁移数据,范围查询资源消耗小 但是存在热点数据问题
离散分片:分片均匀,并发强 缺点:移植性差
- 当当、美团正在做这方面的工作
- mysql Dump命令生成backup.sql文件, 控制台:mysql -f backup.sql或Source backup.sql
1、用好ER表,子表跟随父表
2、善用全局表 冗余数据
3、注解方式
6.5、Mycat的分布式事务?XA ?20181222 如何保证强一致性
两阶段提交 弱XA的两阶段提交
TCC补偿机制
当你的才华还撑不起你的野心时,就应该静下心来学习
推荐阅读
- 项目中常用的19条MySQL优化
- MySQL|MySQL备份与恢复+策略方案选择
- MySQL|MySQL 读写分离的基本概念和实现方式
- mysql|mysql 读写分离_详解MySQL读写分离
- mysql优化|MySQL到底怎么优化?
- MySQL主从复制与读写分离
- 如何利用MHA+ProxySQL实现读写分离和负载均衡
- MySQL优化的5个维度
- Mycat实现MySQL读写分离