分库分表和主从同步

This commit is contained in:
estomm
2021-09-02 10:23:40 +08:00
parent 27cb93f5db
commit 1a5e5f2b27
8 changed files with 112 additions and 30 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

View File

@@ -1,35 +1,141 @@
# MySQL分库分表
## 分表方式
# MySQL分库分表和主从分离
## 1 概述
### 分库分表的原因
当一张表的数据达到几千万时,查询一次所花的时间会变多,如果有联合查询的话,有可能会死在那儿了。分表的目的就在于此,减小数据库的负担,缩短查询时间。
* 用户请求量太大。因为单服务器TPS内存IO都是有限的。解决方法分散请求到多个服务器上 其实用户请求和执行一个sql查询是本质是一样的都是请求一个资源只是用户请求还会经过网关路由http服务器等。
* 单库太大。单个数据库处理能力有限单库所在服务器上磁盘空间不足单库上操作的IO瓶颈 解决方法:切分成更多更小的库
* 单表太大。CRUD都成问题索引膨胀查询超时。解决方法切分成多个数据集更小的表。
### 分库分表的形式
* 单库单表。单库单表是最常见的数据库设计,例如,有一张用户(user)表放在数据库db中所有的用户都可以在db库中的user表中查到。
* 单库多表。随着用户数量的增加user表的数据量会越来越大当数据量达到一定程度的时候对user表的查询会渐渐的变慢从而影响整个DB的性能。如果使用mysql, 还有一个更严重的问题是当需要添加一列的时候mysql会锁表期间所有的读写操作只能等待。可以通过某种方式将user进行水平的切分产生两个表结构完全一样的user_0000,user_0001等表user_0000 + user_0001 + …的数据刚好是一份完整的数据。
* 多库多表。随着数据量增加也许单台DB的存储空间不够随着查询量的增加单台数据库服务器已经没办法支撑。这个时候可以再对数据库进行水平区分。
## 2 分库分表
### 水平切分
水平切分又称为 Sharding它是将同一个表中的记录拆分到多个结构相同的表中。
* 水平分库:系统绝对并发量上来了,分表难以根本上解决问题,并且还没有明显的业务归属来垂直分库。
* 水平分表系统绝对并发量并没有上来只是单表的数据量太多影响了SQL效率加重了CPU负担以至于成为瓶颈。
当一个表的数据不断增多时Sharding 是必然的选择,它可以将数据分布到集群的不同节点上,从而缓存单个数据库的压力。
![](image/2021-09-02-00-50-45.png)
* 主要特点
* 每个库的结构都一样;
* 每个库的数据都不一样,没有交集;
* 所有库的并集是全量数据;
### 垂直切分
垂直切分是将一张表按列切分成多个表,通常是按照列的关系密集程度进行切分,也可以利用垂直切分将经常被使用的列和不经常被使用的列切分到不同的表中。
在数据库的层面使用垂直切分将按数据库中表的密集程度部署到不同的库中,例如将原来的电商数据库垂直切分成商品数据库、用户数据库等。
如果把业务切割得足够独立,那把不同业务的数据放到不同的数据库服务器将是一个不错的方案,而且万一其中一个业务崩溃了也不会影响其他业务的正常进行,并且也起到了负载分流的作用,大大提升了数据库的吞吐能力。
* 垂直分库:系统绝对并发量上来了,并且可以抽象出单独的业务模块。
* 垂直分表系统绝对并发量并没有上来表的记录并不多但是字段多并且热点数据和非热点数据在一起单行数据所需的存储空间较大。以至于数据库缓存的数据行减少查询时会去读磁盘数据产生大量的随机读IO产生IO瓶颈。
![](image/2021-09-02-00-51-00.png)
* 主要特点
* 每个库的结构都不一样;
* 每个库的数据也不一样,没有交集;
* 所有库的并集是全量数据;
### Sharding 策略
- 哈希取模hash(key) % N
- 范围:可以是 ID 范围也可以是时间范围;
- 映射法:哈希取模hash(key) % N。Snowflake算法。
- HASH取模方法优点 能保证数据较均匀的分散落在不同的库、表中,减轻了数据库压力
- HASH取模方法缺点 扩容麻烦、迁移数据时每次都需要重新计算hash值分配到不同的库和表
- 范围法:可以是 ID 范围、时间范围、地理范围。
- RANGE方法优点 扩容简单,提前建好库、表就好
- RANGE方法缺点 大部分读和写都访会问新的数据有IO瓶颈这样子造成新库压力过大不建议采用。
- 映射表:使用单独的一个数据库来存储映射关系。
### Sharding 工具
* sharding-spherejar前身是sharding-jdbc
* TDDLjarTaobao Distribute Data Layer
* Mycat中间件。
### Sharding 存在的问题
1. 事务问题。使用分布式事务来解决,比如 XA 接口。
2. 连接.可以将原来的连接分解成多个单表查询,然后在用户程序中进行连接。
2. 连接可以将原来的连接分解成多个单表查询,然后在用户程序中进行连接。
3. ID 唯一性
- 使用全局唯一 IDGUID
- 为每个分片指定一个 ID 范围。
- 分布式 ID 生成器 (如 Twitter 的 Snowflake 算法)能够计算出sharding ID
- 分布式 ID 生成器能够计算出sharding ID
4. 维度的问题。假如用户购买了商品,需要将交易记录保存取来,如果按照用户的纬度分表,则每个用户的交易记录都保存在同一表中,所以很快很方便的查找到某用户的 购买情况,但是某商品被购买的情况则很有可能分布在多张表中,查找起来比较麻烦。反之,按照商品维度分表,可以很方便的查找到此商品的购买情况,但要查找 到买人的交易记录比较麻烦。所以常见的解决方式有:
* 通过扫表的方式解决,此方法基本不可能,效率太低了。
* 记录两份数据,一份按照用户纬度分表,一份按照商品维度分表。
* 通过搜索引擎解决,但如果实时性要求很高,又得关系到实时搜索。
### 分库分表时机
单表1000万条记录一下,写入读取性能是比较好的. 这样在留点buffer,那么单表全是数据字型的保持在800万条记录以下, 有字符型的单表保持在500万以下。
不管是IO瓶颈还是CPU瓶颈最终都会导致数据库的活跃连接数增加进而逼近甚至达到数据库可承载活跃连接数的阈值。在业务Service来看就是可用数据库连接少甚至无连接可用。
* IO 瓶颈
* 第一种磁盘读IO瓶颈热点数据太多数据库缓存放不下每次查询时会产生大量的IO降低查询速度 -> 分库和垂直分表。
* 第二种网络IO瓶颈请求的数据太多网络带宽不够 -> 分库。
* CPU瓶颈
* 第一种SQL问题如SQL中包含joingroup byorder by非索引字段条件查询等增加CPU运算的操作 -> SQL优化建立合适的索引在业务Service层进行业务计算。
* 第二种单表数据量太大查询时扫描的行太多SQL效率低CPU率先出现瓶颈 -> 水平分表。
## 3 主从同步
### 主从分离
将数据库的写操作和读操作进行分离, 使用多个从库副本Slaver Replication负责读使用主库Master负责写 从库从主库同步更新数据,保持数据一致。架构上就是数据库主从同步。 从库可以水平扩展所以更多的读请求不成问题。通过此方式可以有效的提高DB集群的 QPS.
1. **读写分离**:在从服务器可以执行查询工作,降低主服务器压力;(主库写,从库读,降压)读写分离
2. **容灾**:在从主服务器进行备份,避免备份期间影响主服务器服务;容灾
3. **提高可用性**:当主服务器出现问题时,可以切换到从服务器。提高可用性
### 主从同步过程
Mysql服务器之间的主从同步是基于二进制日志BINLOG机制主服务器使用二进制日志来记录数据库的变动情况从服务器通过读取和执行该日志文件来保持和主服务器的数据一致。
![](image/2021-09-02-10-11-38.png)
1. SQL语句操作变化存入BinLog日志中
2. slave上线连接到master服务器进行数据同步
3. dump thread线程把Binlog数据发送到slave中
4. slave启动之后创建一个I/O线程读取master传过来的Binlog内容并写入到Relay log.
5. slave还会创建一个SQL线程从Relay log里面读取内容从Exec_Master_Log_Pos位置开始执行读取到的更新事件将更新内容写入到slave的db.
### 主从同步方法
![](image/2021-09-02-10-14-13.png)
1. 异步复制。MySQL 默认的复制策略Master处理事务过程中将其写入Binlog就会通知Dump thread线程处理然后完成事务的提交不会关心是否成功发送到任意一个slave中
![](image/2021-09-02-10-15-31.png)
2. 半同步复制。Master处理事务过程中提交完事务后必须等至少一个Slave将收到的binlog写入relay log返回ack才能继续执行处理用户的事务。
![](image/2021-09-02-10-16-47.png)
3. 增强半同步复制。半同步的问题是因为等待ACK的点是Commit之后此时Master已经完成数据变更用户已经可以看到最新数据当Binlog还未同步到Slave时发生主从切换那么此时从库是没有这个最新数据的用户又看到老数据。增强半同步将等待ACK的点放在提交Commit之前此时数据还未被提交外界看不到数据变更此时如果发送主从切换新库依然还是老数据不存在数据不一致的问题。
![](image/2021-09-02-10-17-53.png)
4. 组复制。MySQL在引擎层完成Prepare操作写Redo日志之后会被MySQL的预设Hook拦截进入MGR层。MGR层将事务信息打包通过Paxos协议发送到全部节点上只要集群中过半节点回复ACK那么将告诉所有节点数据包同步成功然后每个节点开始自己认证certify通过就开始写Binlog提交事务或者写relay log数据同步如果认证不通过则rollback。总结MGR内部实现了分布式数据一致性协议paxos通过其来保证数据一致性。
![](image/2021-09-02-10-19-31.png)

View File

@@ -1,24 +0,0 @@
## 主从库
### 主从复制
主要涉及三个线程binlog 线程、I/O 线程和 SQL 线程。
- **binlog 线程** 负责将主服务器上的数据更改写入二进制日志Binary log中。
- **I/O 线程** 负责从主服务器上读取二进制日志并写入从服务器的中继日志Relay log
- **SQL 线程** 负责读取中继日志解析出主服务器已经执行的数据更改并在从服务器中重放Replay
![](image/2021-09-02-00-54-18.png)
### 读写分离
主服务器处理写操作以及实时性要求比较高的读操作,而从服务器处理读操作。
读写分离能提高性能的原因在于:
- 主从服务器负责各自的读和写,极大程度缓解了锁的争用;
- 从服务器可以使用 MyISAM提升查询性能以及节约系统开销
- 增加冗余,提高可用性。
读写分离常用代理方式来实现,代理服务器接收应用层传来的读写请求,然后决定转发到哪个服务器。
![](image/2021-09-02-00-54-06.png)