本人查看了网站很多的资料,通过自己的理解写下了这篇文章,希望会对大家有所帮助。
本文以网站群文章系统为例。根据各自的业务需求自行调整。
随着数据量的逐步增大,数据库性能显著降低,我们就要对数据库进行扩容,这里介绍数据库水平切分的架构实践。
一、文章系统
文章是一个非常常见的业务,其核心元数据为:
Article(aid,cid,sid,status…)
其中:
-
aid为文章ID,主键。
-
cid为分类ID
-
sid为网站site_id
数据库设计上,一般来说在业务初期,单表就能够搞定这个需求,典型的架构设计为:
当数据量越来越大时,需要多文章表进行扩容:
二、分表扩容带来的问题
数据库扩容后插入、查询等操作数据需要先定位到数据所在的表:
对于aid属性上的查询可以直接定位到表,假设访问aid=666的数据,取模后能够直接定位content_0000:
对于非aid属性上的查询,例如cid、sid属性上的查询,就悲剧了:
假设访问cid=666的数据,由于不知道数据落在哪个表上,往往需要遍历所有表,当分表数量很多时,性能会显著降低。
三、水平切分
1、索引表法
aid能直接定位到库,cid不能直接定位到库,如果通过cid能查询到aid,问题解决。
解决方案:
-
建立一个索引表记录cid->aid的映射关系。
-
用cid来访问时,先通过索引表查询到aid,再定位相应的表。
-
索引表属性较少,可以容纳非常多数据,一般不需要分库。
潜在不足:多一次数据库查询,性能下降一倍。
2、缓存映射法
访问索引表性能较低,把映射关系放在缓存里性能更佳。
解决方案:
-
cid查询先到cache中查询aid,再根据aid定位数据库。
-
假设cache miss,扫描全库获取cid对应的uid,放入cache.
-
cid到aid的映射关系不会变化,映射关系一旦放入缓存,不会更改,无需淘汰,缓存命中率超高。
潜在不足:多一次cache查询。
3、基因法
什么是分库基因?
通过sid分表,假设分为16个表,采用sid%16的方式来进行数据库路由,这里的sid%16,其本质是aid的最后4个bit决定这行数据落在哪个库上,这4个bit,就是分库基因。
什么是基因法分库?
在“1对多”的业务场景,使用“1”分库,在“多”的数据id生成时,id末端加入分库基因,就能同时满足“1”和“多”的分库查询需求。
为了解决分表扩容带来的问题,我们要对网站编号sid(site_id)抽取“基因”,融入aid、cid...中:
通过基因生成的aid和cid通过最后4bit的基因我们就可以直接定位到数据所在的表。
总结:
若使用基因法分表扩容建议大家提前做好容量预估,一旦定型后再进行扩容时,需要从新计算基因导入数据,这点根据自己的业务需求设计。
如果查询条件不是基因生成的id,建议使用搜索服务,比如ElasticSearch。
通过基因生成的id我们可以直接定位到所在的表,减少了索引和缓存带来的不足。
基因抽取示例: