主题
数据库分离技术
冷热分离
冷热分离就是在处理数据时将数据库分成冷库和热库,冷库存放那些走到终态、不常使用的数据,热库存放还需要修改、经常使用的数据。
区分冷热数据
- 时间维度:比如更新时间超过3个月的数据为冷数据。
- 状态维度:比如订单状态为已完成的标识为冷数据。
- 多维度结合:比如订单状态为已完成且更新时间超过3个月为冷数据。
触发冷热分离
- 直接修改业务代码:在每次数据变更时添加判断逻辑。
- 监听数据库变更日志:另写一个订阅数据库日志变更的程序来处理。
- 定时扫描数据库:另写一个程序定时扫描数据库来处理。
| 方法 | 优点 | 缺点 |
|---|---|---|
| 直接修改业务代码 | 代码灵活可控; 保证实时性 | 无法处理按时间维度区分冷热数据的场景; 需要对所有更新数据的位置进行修改(这也体现出统一入口的好处) |
| 监听数据库变更日志 | 与业务代码解耦; 低延时 | 无法处理按时间维度区分冷热数据的场景; 需要考虑业务代码与变更代码同时操作同一数据带来的并发问题 |
| 定时扫描数据库 | 与业务代码解耦; 可以覆盖按时间维度区分冷热数据的场景 | 不能做到实时性 |
对于不按时间维度区分冷热数据的场景,如果业务代码简单,入口统一,也可直接修改业务代码;如果业务代码复杂,不想修改业务代码,也可使用监听数据库变更日志的方式。
实际业务中,需要冷热分离的数据对实时性要求并不高,所以大部分场景都使用定时扫描数据库的方式,并且该方式拥有更好的异常处理能力。
分离冷热数据
不管用何种方式触发冷热数据分离,都需要将数据插入到冷数据库,再将热数据库中的数据删除。这就带来数据的最终一致性问题。插入或删除的时候出现异常,要如何解决?
比如,对于直接修改业务代码和监听数据库日志的方式,如果因为冷库发生了网络中断而一直插入失败,这条数据要如何处理?最简单的方式是把这条数据临时保存在本地,或推送到队列,或给表添加标志位。不管怎样,都得再开发程序来处理异常数据。而采用定时扫描数据库的方式就不会有这种问题,这次插入异常,下次扫描再插入就行了,一套代码搞定。
对于数据量很大的情况,如果单线程不能在限定时间内处理完成,可以使用多线程分段处理。扫描的时候查询语句加入范围查询,比如根据ID范围或时间段查询,每个线程查询不同的范围。
使用冷热数据
由于冷热数据已经分离到两个数据库,所以同时查询冷数据和热数据会比较麻烦,如果真有这种需求,就需要在业务代码中进行合并处理,或者直接🔪这个需求。
读写分离
将数据库分为主库和从库,主库只写,从库只读,主从库之间通过某种机制进行数据同步。
数据同步方案
- 直接修改业务代码
- 监听数据库变更日志
常规演化步骤:
- 前期业务简单,可以直接修改业务代码来同步数据。
- 后期业务扩张,可能多个地方都会修改数据,即使统一 API 入口,也保不准有些人偷偷直连数据库修改数据😂。这种情况就要解耦,改为使用监听数据库变更日志的方法同步数据。
- 后来公司部门扩展,多个部门都需要这些数据来满足自己的业务需求,但是其数据库模型不一定和此数据库模型一致,可能只需要部分字段,只提供 API 的方式供其它部门使用数据已不可行。这种情况消息队列就发挥作用了,把变更数据推送到队列,需要的部门订阅队列获取数据并用于建设自身数据库。
查询分离
查询分离基本和读写分离类似,只不过查询分离使用的是两套不同的存储系统,通常是因为主库不支持一些查询方式或查询非常耗时。
比如舆情文本数据,需要多标签查询和全文搜索等,但 MySQL 对列表查询支持有限,全文搜索就不用想了。所以可以把 Elasticsearch 充当“从库”,用于查询。
分库分表
当一张表的数据量过多时,查询会比较耗时,这种情况可以将表拆分为多个表,将库拆分为多个库。
- 水平分表:表结构是相同的,将多行数据分散到多个表中。
- 垂直分表:表结构是不同的,将表的字段分开存储。比如舆情数据库,有舆情正文文本的字段,有与正文相关的分析字段(如情感分类,主题分类等),大部分的查询场景不需要这个很长的正文,并且这个长文本和分析字段放在一起也会影响查询性能。这种情况就可以把正文等不经常用的字段放在一个表里,把经常查询的字段放在另一个表里。
分库分表中间件
因为数据已经分布在不同的库或表中,所以需要动态组合SQL语句,合并查询结果等操作。可以通过两类中间件来实现。
Proxy模式
增加一层中间代理来处理,好处是不会侵入业务代码。如:MyCat、cobar、ShardingSphere-Proxy等。

Client模式
在客户端引入相关的包来处理,好处是代码灵活可控。如:ShardingSphere-JDBC。

分片主键
分库分表的关键是确定分片主键。一般需要考虑三个要求:
- 数据尽量均匀分布在不同的表或库
- 跨库查询操作尽可能少
- 所选字段的值不会变
一般来说,可以使用数据库的自增主键或业务主键来作为分片主键。
分片策略
| 分片策略 | 描述 | 举例 |
|---|---|---|
| 范围分片 | 按数据范围值做分片。 | 例如按自增ID分片,1-99999映射到实例A; 100000-199999映射到实例B。 |
| 哈希分片 | 通过对key进行hash运算再取模一个特定的数据进行分片。取模数一般是 | hash(key) % 2^3,即分成8个表。 |
| 混合分片 | 先按照范围分片,再根据哈希值取模分片。 | t_#userId%10_#hash(userId)%8 ,即分成了 10x8=80 个表。 |
| 一致性哈希分片 | 将存储节点和数据都映射到一个首尾相连的哈希环上,以尽可能减少数据迁移。多用于分布式系统。 | 参考 |
以上分片策略如何选择?只需要考虑一点:后续数据量增加,需要把表分得更细,此时保证迁移的数据尽量少即可。