千万级并发架构下,关系型数据库应该如何优化?大厂是如何做分库分表的! (4)

image-20210715160141288

图6-12 按照范围分片

按范围分片,其实就是基于数据表的业务特性,按照某种范围拆分,这个范围的有很多含义,比如:

时间范围,比如我们按照数据创建时间,按照每一个月保存一个表。基于时间划分还可以用来做冷热数据分离,越早的数据访问频次越少。

区域范围,区域一般指的是地理位置,比如一个表里面存储了来自全国各地的数据,如果数据量较大的情况下,可以按照地域来划分多个表。

数据范围,比如根据某个字段的数据区间来进行划分。

如图6-7所示,表示按照数据范围进行拆分。

image-20210714230103964

图6-7

范围分片最终要的是选择一个合适的分片键,这个是否合适来自于业务需求,比如之前有个学员是在做智能家居的,他们卖的是硬件设备,这些设备会采集数据上报到服务器上,当来自全国范围的数据统一保存在一个表中后,数据量达到了亿级别,所以这种场景比较适合按照城市和地域来拆分。

分库分表实战

为了让大家理解分库分表以及实操,我们通过一个简单的案例来演示一下。代码详见:springboot-split-table-example项目

假设存在一个用户表,用户表的字段如下。

该表主要提供注册、登录、查询、修改等功能。

image-20210715201725385

图6-8

该表的具体的业务情况如下(需要注意,在进行分表之前,需要了解业务层面对这个表的使用情况,然后再决定使用什么样的方案,否则脱离业务去设计技术方案是耍流氓)

用户端: 前台访问量较大,主要涉及两类请求:

用户登录,面向C端,对可用性和一致性要求较高,主要通过login_name、email、phone来查询用户信息,1%的请求属于这种类型

用户信息查询,登录成功后,通过uid来查询用户信息,99%属于这种类型。

运营端: 主要是运营后台的信息访问,需要支持根据性别、手机号、注册时间、用户昵称等进行分页查询,由于是内部系统,访问量较低,对可用性一致性要求不高。

根据uid进行水平分表

由于99%的请求是基于uid进行用户信息查询,所以毫无疑问我们选择使用uid进行水平分表。那么这里我们采用uid的hash取模方法来进行分表,具体的实施如图6-9所示,根据uid进行一致性hash取模运算得到目标表进行存储。

image-20210715204044151

图6-9

按照图6-9的结构,分别复制user_info表,重新命名为01~04,如图6-10所示。

image-20210715204629468

图6-10 如何生成全局唯一id

当完成上述动作后,就需要开始开始落地实施,这里需要考虑在数据添加、修改、删除时,要正确路由到目标数据表,其次是老数据的迁移。

老数据迁移,一般我们是写一个脚本或者一个程序,把旧表中的数据查询出来,然后根据分表规则重新路由分发到新的表中,这里不是很复杂,就不做展开说明,我们重点说一下数据添加/修改/删除的路由。

在实施之前,我们需要先考虑一个非常重要的问题,就是在单个表中,我们使用递增主键来保证数据的唯一性,但是如果把数据拆分到了四个表,每个表都采用自己的递增主键规则,就会存在重复id的问题,也就是说递增主键不是全局唯一的。

我们需要知道一个点是,user_info虽然拆分成了多张表,但是本质上它应该还是一个完整的数据整体,当id存在重复的时候,就失去了数据的唯一性,因此我们需要考虑如何生成一个全局唯一ID。

如何实现全局唯一ID

全局唯一ID的特性就是能够保证ID的唯一性,那么基于这个特性,我们可以轻松找到很多的解决方案。

数据库自增ID(定义全局表)

UUID

Redis的原子递增

Twitter-Snowflake算法

美团的leaf

MongoDB的ObjectId

百度的UidGenerator

分布式ID的特性

唯一性:确保生成的ID是全局唯一的。

有序递增性:确保生成的ID是对于某个用户或者业务是按一定的数字有序递增的。

高可用性:确保任何时候都能正确的生成ID。

带时间:ID里面包含时间,一眼扫过去就知道哪天的数据

数据库自增方案

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzdsws.html