Skip to content
0

数据库分离技术

冷热分离

冷热分离就是在处理数据时将数据库分成冷库和热库,冷库存放那些走到终态、不常使用的数据,热库存放还需要修改、经常使用的数据。

区分冷热数据

  1. 时间维度:比如更新时间超过3个月的数据为冷数据。
  2. 状态维度:比如订单状态为已完成的标识为冷数据。
  3. 多维度结合:比如订单状态为已完成且更新时间超过3个月为冷数据。

触发冷热分离

  1. 直接修改业务代码:在每次数据变更时添加判断逻辑。
  2. 监听数据库变更日志:另写一个订阅数据库日志变更的程序来处理。
  3. 定时扫描数据库:另写一个程序定时扫描数据库来处理。
方法优点缺点
直接修改业务代码代码灵活可控;
保证实时性
无法处理按时间维度区分冷热数据的场景;
需要对所有更新数据的位置进行修改(这也体现出统一入口的好处)
监听数据库变更日志与业务代码解耦;
低延时
无法处理按时间维度区分冷热数据的场景;
需要考虑业务代码与变更代码同时操作同一数据带来的并发问题
定时扫描数据库与业务代码解耦;
可以覆盖按时间维度区分冷热数据的场景
不能做到实时性

对于不按时间维度区分冷热数据的场景,如果业务代码简单,入口统一,也可直接修改业务代码;如果业务代码复杂,不想修改业务代码,也可使用监听数据库变更日志的方式。

实际业务中,需要冷热分离的数据对实时性要求并不高,所以大部分场景都使用定时扫描数据库的方式,并且该方式拥有更好的异常处理能力。

分离冷热数据

不管用何种方式触发冷热数据分离,都需要将数据插入到冷数据库,再将热数据库中的数据删除。这就带来数据的最终一致性问题。插入或删除的时候出现异常,要如何解决?

比如,对于直接修改业务代码和监听数据库日志的方式,如果因为冷库发生了网络中断而一直插入失败,这条数据要如何处理?最简单的方式是把这条数据临时保存在本地,或推送到队列,或给表添加标志位。不管怎样,都得再开发程序来处理异常数据。而采用定时扫描数据库的方式就不会有这种问题,这次插入异常,下次扫描再插入就行了,一套代码搞定。

对于数据量很大的情况,如果单线程不能在限定时间内处理完成,可以使用多线程分段处理。扫描的时候查询语句加入范围查询,比如根据ID范围或时间段查询,每个线程查询不同的范围。

使用冷热数据

由于冷热数据已经分离到两个数据库,所以同时查询冷数据和热数据会比较麻烦,如果真有这种需求,就需要在业务代码中进行合并处理,或者直接🔪这个需求。

读写分离

将数据库分为主库和从库,主库只写,从库只读,主从库之间通过某种机制进行数据同步。

数据同步方案

  1. 直接修改业务代码
  2. 监听数据库变更日志

常规演化步骤:

  1. 前期业务简单,可以直接修改业务代码来同步数据。
  2. 后期业务扩张,可能多个地方都会修改数据,即使统一 API 入口,也保不准有些人偷偷直连数据库修改数据😂。这种情况就要解耦,改为使用监听数据库变更日志的方法同步数据。
  3. 后来公司部门扩展,多个部门都需要这些数据来满足自己的业务需求,但是其数据库模型不一定和此数据库模型一致,可能只需要部分字段,只提供 API 的方式供其它部门使用数据已不可行。这种情况消息队列就发挥作用了,把变更数据推送到队列,需要的部门订阅队列获取数据并用于建设自身数据库。

查询分离

查询分离基本和读写分离类似,只不过查询分离使用的是两套不同的存储系统,通常是因为主库不支持一些查询方式或查询非常耗时。

比如舆情文本数据,需要多标签查询和全文搜索等,但 MySQL 对列表查询支持有限,全文搜索就不用想了。所以可以把 Elasticsearch 充当“从库”,用于查询。

分库分表

当一张表的数据量过多时,查询会比较耗时,这种情况可以将表拆分为多个表,将库拆分为多个库。

  • 水平分表:表结构是相同的,将多行数据分散到多个表中。
  • 垂直分表:表结构是不同的,将表的字段分开存储。比如舆情数据库,有舆情正文文本的字段,有与正文相关的分析字段(如情感分类,主题分类等),大部分的查询场景不需要这个很长的正文,并且这个长文本和分析字段放在一起也会影响查询性能。这种情况就可以把正文等不经常用的字段放在一个表里,把经常查询的字段放在另一个表里。

分库分表中间件

因为数据已经分布在不同的库或表中,所以需要动态组合SQL语句,合并查询结果等操作。可以通过两类中间件来实现。

Proxy模式

增加一层中间代理来处理,好处是不会侵入业务代码。如:MyCat、cobar、ShardingSphere-Proxy等。

Proxy模式

Client模式

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

Client模式

分片主键

分库分表的关键是确定分片主键。一般需要考虑三个要求:

  • 数据尽量均匀分布在不同的表或库
  • 跨库查询操作尽可能少
  • 所选字段的值不会变

一般来说,可以使用数据库的自增主键或业务主键来作为分片主键。

分片策略

分片策略描述举例
范围分片按数据范围值做分片。例如按自增ID分片,1-99999映射到实例A;
100000-199999映射到实例B。
哈希分片通过对key进行hash运算再取模一个特定的数据进行分片。取模数一般是 2n,扩容后只需要迁移大约一半的数据。hash(key) % 2^3,即分成8个表。
混合分片先按照范围分片,再根据哈希值取模分片。t_#userId%10_#hash(userId)%8 ,即分成了 10x8=80 个表。
一致性哈希分片将存储节点和数据都映射到一个首尾相连的哈希环上,以尽可能减少数据迁移。多用于分布式系统。参考

以上分片策略如何选择?只需要考虑一点:后续数据量增加,需要把表分得更细,此时保证迁移的数据尽量少即可。