1.为什么需要分库分表?
数据库sharding可以分为两个部分,1是数据库的垂直切分,即分库,2是水平切分,即分表。简单的说,如果一个单点数据库的访问频率特别高,负载特别大的情况下,分库的优化能够分散单点数据库的压力,如果单张表的数据量特别大,分表能够在进行读写的时候提升效率。
2.如果进行分库分表
举个��,现有一个database,若干table,每一个table中分别存储着同一个对象的不同的属性,比如一个人身份信息储存在一个table中,职业简历储存在一个table中,家庭成员信息储存在一个table中…那么在进行分库的时候,就需要找到一个聚合根(简单理解为不同对象所具有的相同属性的唯一标识),然后根据聚合根将这个对象所关联的所有对象形成一个聚合划分到一个database中。
比如现在有下面几个结构:
个人信息表:
t_user_info{ int64_t user_id; //(主键), string user_name; int user_sex; int user_age; int user_hight; int user_weight; };
如果database里面是根据个人各方面信息,那么如果user_id能够满足根据唯一的user_id能够在database中找到完整的个人信息,那么user_id便可以作为聚合根;
换一个维度,添加一张家庭成员表:
t_family_relation{ int64_t family_id; int64_t family_user_id; int family_relationship }
如果增加一个由家庭与各个成员的关系列表,那么可以确定每一个成员都属于一个家庭,不同的成员之间也可能属于同一个家庭,那么可能存在情况,如果需要查询同一个家庭中的不同的user,就需要保证在同一个家庭的不同的user需要存在同一个table中,所以,如果family_id能够满足根据唯一的family_id能够在database中找到家庭成员信息,那么family_id便可以作为聚合根。
分表的概念要简单得多,如果一张表的数据量特别大,那么在进行读写的时候需要查询的范围就会特别大,如果现在一个表中包含5000w条数据,和100个包含50w条数据的表,进行读写,必然后者效率会高很多。
所以在具体进行分库分表的时候,方法很多:
散列取膜的方法,首先获取一个index,比如上面按照family_id获取一个index,假定千万级数据,family_id 从10000000开始,分库分表的目标为m库n表。
int getDatabaseIndex(int64_t family_id, int m){ return family_id/m%m; //得出的结果也是在[0,n)之间 } int getTableIndex(int64_t family_id, int n){ return family_id/n%n; //获得的index便是在[0,n)之间 }
然后在根据index获得对应的数据库连接,直接指向对应的数据库,对应的表
这样做的优点是数据库分布均匀,但是在数据迁移或者进一步拆分的时候得按照取膜的方法对数据进行还原,或者向下兼容新的拆分策略
顺序方法,还是上面的例子
int getDatabaseIndex(int64_t family_id, int m){ return family_id/m; } int getTableIndex(int64_t family_id, int n){ return family_id/n; }
如果m=n=1000的话,这样的结果便是,0-999属于一个集合,10000-19999属于一个集合,直到9990000-99999999属于一个集合,这样的优点在于数据整体性高,可移植性强,缺点在于可能存在分布不均匀的情况,
还有一种方法是数据库映射
就是建立一个DB,这个DB单独保存user_id到DB的映射关系,每次访问数据库的时候都要先查询一次这个数据库,以得到具体的DB信息,然后才能进行我们需要的查询操作,虽然,这样能做到数据的一一对应,但是这样做也降低了查询性能。