分库和分表是架构必经之路,我想问问你是怎么分库和分表?

很多系统在设计之初就没有考虑过后期的分库与分表,甚至开发团队没有架构和DBA人员,开发团队也比较年轻,对于数据库的架构定义非常随意,满足当前需求即可。

实际上数据库结构等同于建筑里面的地基,地基没有打好,后面的优化都是徒劳的,最终不得不重构数据库结构。

那么你是怎样分库分表的?

任何系统数据流都是漏斗形状的,数据库是漏斗末端,架构设计是尽量在前端计算,合并,拆分,分流,缓存,最终将有价值的数据写入数据库。数据库的访问是结果集越小越好。

基于这种认识,通常分库和分表,我们想到的就是首先垂直分表,这种方式简单易操作。

当前(本年度数据库)(热数据)

2019年数据

2018年数据

以此类推

或者按照月份分表

当前(热数据)

10月数据

9月数据

以此类推

这样分表可以缩小结果集,能快速解决查询瓶颈问题。但是新的挑战来了,由于分表后,索引是独立不连续的,历史数据的查询或遍历数据变的复杂了,要么使用联合查询,要么一张表一张表的遍历。

同理水平分表也是粗暴的将一些尺寸较大的列独立成新表,以降低单个表的容量尺寸。

如果是单纯的数据查找,还是能忍受,我们可能根据时间来选择查询的表,如果是复杂的SQL操作,就只能逐一查询,在程序中二次计算,合并等等操作。

这种分库或分表的思路,理论上属于数据归档。将热数据放在当前数据库中,将很少查询的冷数据放在另一个库中。但是对于 user 这种表就无能为力,你不知道那个用户什么时候会做登录操作。

分表需要从业务角度考虑

分表需要从业务角度考虑,数据库服务于业务逻辑。

由于我即负责产品也负责架构,长期的工作中,总结出一套分库分表的策略。

我分表策略是:从UI角度出发

从业务流角度出发

举例一,用户表分表?

用户表怎样水平拆分呢? 用户登录的逻辑是这样的,第一个UI 输入用户名和密码,提交后验证密码,用户是否过期,记录登录时间,IP地址。第二个UI,载入用户资料,包括用户姓名,年龄,性别等等…..

数据库我是这样设计的:

+-----------------------------------------------+
| Member |
+-----------------------------------------------+
| id | username | password | ip | ctime | mtime |
+-----------------------------------------------+
就这么简单,会员表服务于第一UI
+-----------------------------------------------+
| Profile
+-----------------------------------------------+
| id | member_id | name | sex | age | mtime |…… |
+-----------------------------------------------+

服务于第二个UI,通过 member_id 关联数据

怎么样?用户登录过程并不会去访问 profile 表,只有登录成功才会访问。

用户表怎样垂直拆分呢? 将 username 做crc32/md5/sha1 运算,使用哪种随你,取出第一个字符用于分表。

例如 neo, netkiller 两个用户被分到 member_n 表中,jerry,jam 被分到member_j 表中,与支配套的还有 profile_n, profile_j 等等。我们甚至还可以使用外键约束 member_n 和 profile_n 两个表。

当用户登录是,对用户ID做一次 hash 运算,就知道去哪个表中找到该用户的数据。

举例二,商品表如何分表?

商品信息表数据量非常大,我们可以基于品类分库或分表,我们UI设计中,一般只有首页才会将不同分类的产品聚合到一起。进入品类分类页面后,只会访问该品类的数据表。

这就是从业务流的角度进行分表,用户操作是逐渐被引导至我们想呈现的页面。

举例三,海量用户如何分库?

海量用户分库的思路是,用户被分配到指定数据库,该用户所有的数据都会产生在该数据库中,也可以理解为基于数据库隔离用户。

基于该思路分库,这样表名保持不变。

例如 neo, netkiller 被分配到 schema_n.member, schema_n.profile

例如 jerry, jam 被分配到 schema_j.member, schema_j.profile

一旦用户登录,便被锁定到指定的数据库,接下来所有操作,用户产生的数据,都被存储在该数据库中。索引连续,外键约束,触发器,存储过程,均不影响使用。

使用分区表

使用分区别将分区数据写入挂载的SSD盘上。例如 /opt/data/ 下面挂载了四块SSD,目录名是 partition1~4。

CREATE TABLE your_table (id INT, cdate DATE) engine='InnoDB'
PARTITION BY LIST(YEAR(cdate)) (
PARTITION p2020 VALUES IN (2020) DATA DIRECTORY = '/opt/data/partition1',
PARTITION p1999 VALUES IN (1999) DATA DIRECTORY = '/opt/data/partition2',
PARTITION p1998 VALUES IN (1998) DATA DIRECTORY = '/opt/data/partition3',
PARTITION p1997 VALUES IN (1997, 1996, 1995) DATA DIRECTORY = '/opt/data/partition4'
);

最后总结

分库和分表不是简单的切割,而出需要从业务的角度出发,从产品经理视角,你需要展现什么数据,什么样的数据库结构能更好的为UI服务。或者我们应该设计什么样的UI才能更好的展现数据。