本人一应届生,在去实习的地铁上,看了一篇沈剑老师写数据库水平切分架构思路,看完人清气爽。现在写一篇博客记录下。
引言:
涉及到数据库水平切分问题,主要有以下两种问题:
第一:项目开始之前进行数据库架构设计,考核,规划以后数据库量大的情况有很好的扩展性;
第二:当项目用户量持续增大时,数据库压力过大,相应速度降低,用户体验度降低,这时可以通过优化数据库来解决,即水平切分数据库。
本文以用户登陆为例,来讨论学习数据库水平切分的思路,如有哪里写的不对,望原谅并留言提出,谢谢。
如何切分
项目初期一般都采用单表单库来解决用户登陆功能,但是当数据量大的时候可以考虑数据库水平切分,如何切分?
第一:范围法;
范围法顾名思义,就是通过主键uid来划分,例如1-1kw的数据为一个数据库,1kw-2kw的数据为一个数据库。
优点:
1.切分策略简单,根据uid可以快速定位到哪个数据库上;
2.扩容简单,如果容量持续增大,只要增加数据库服务器即可解决。
缺点:
1.uid必须满足递增的特性;
2.数据量不均,新增的数据库会很少请求到,短时间内浪费内存;
3.请求量不均匀,一般来说,新注册的用户活跃度会比较高,故db2的数据库压力往往比db3的压力大,导致数据库利用率不平衡;
第二:哈希法;
顾名思义,哈希法主要是对uid进行hash算法,根据值来切分数据库。
例如:uid%2=1的存入db1,uid%2=0的存入db2.
优点:
1.切分简单:根据uid直接可以定位到哪个db中;
2.数据量均匀:只要uid是均匀的,数据再各个库中的分配一定是均匀的。
3.请求量均匀:只要uid是均匀的,请求负载在各个库上的分布一定也是均匀的。
缺点:
1.扩容麻烦:需要对uid进行重新hash算法,如何平滑的进行数据迁移是一个需要考量的问题。
切分后带来的问题
切分思路很清晰,但是我们也要考虑到分库后所带来的问题,例如以上如何平滑的迁移数据。这个问题不是在今天讨论的范围之内。
还有一个问题,就是非uid属性username属性查询需求分析。
具体来说:
- 用户方面,前台使用用户名,手机号和emai登陆,如何做好username等属性和uid的映射关系?
思路:建立非uid属性到uid的映射关系
解决方案:
1.索引表法:建立一个索引记录表的映射关系表,用username访问时,先在索引表查询uid然后再定位到相应的表。索引表属性尽量少,可以容纳较多数据,一般不需要分库,如果需要分库可以username来进行分库;
2.缓存映射法:可以将映射关系存入缓存中3.username属性生成uid,用户注册时,username通过一定规则来生成uid;
4.username基因嵌入到uid中,假设分8个库,采用uid%8路由,潜台词是uid最后3个bit决定这条数据落在哪个库上,这三个bit就是所谓的基因。如果uid设计长度为64位,在用户注册时先根据username生成3bit基因,同时生成61bit的全局唯一id,作为用户标识,然后拼接为最后的64bit的uid。
如果用username登陆时,先通过函数将username复原为3bit基因,然后通过3bit基因定位到库。
- 运营方面,后台访问。
运营基本都是批量分页的访问,这样返回数据量大,比较消耗数据库性能。但是运营的请求也不多,但是一旦查询会影响线上的其他用户的查询。
解决方法:
前后端分离,业务架构基本不变,
— 对于运营的查询只需要抽取独立的web/service/db来支持,解决系统之间的耦合或者直接去掉service层,
— 直接调用dao层查询数据,不需要反向代理,不需要集群冗余,不需要访问实时库,可以通过MQ或者线下异步同步数据,
—-如果数据量非常大的情况,可以使用更高延迟的索引外置或者HIVE涉及方案。