传统的分库分表
传统的分库分表都是通过应用层逻辑实现的,对于数据库层面来说,都是普通的表和库。
分库
分库的原因
首先,在单台数据库服务器性能足够的情况下,分库对于数据库性能是没有影响的。在数据库存储上,database只起到一个namespace的作用。database中的表文件存储在一个以database名命名的文件夹中。比如下面的employees数据库:
mysql> show tables in employees;
+---------------------+
| Tables_in_employees |
+---------------------+
| departments |
| dept_emp |
| dept_manager |
| employees |
| salaries |
| titles |
+---------------------+
在操作系统中看是这样的:
ls /usr/local/var/mysql/employees db.opt dept_emp.frm dept_manager.ibd salaries.frm titles.ibd departments.frm dept_emp.ibd employees.frm salaries.ibd departments.ibd dept_manager.frm employees.ibd titles.frm
database不是文件,只起到namespace的作用,所以MySQL对database大小当然也是没有限制的,而且对里面的表数量也没有限制
所以,为什么要分库呢?
答案是为了解决单台服务器的性能问题,当单台数据库服务器无法支撑当前的数据量时,就需要根据业务逻辑紧密程度把表分成几撮,分别放在不同的数据库服务器中以降低单台服务器的负载。
分库一般考虑的是垂直切分,除非在垂直切分后,数据量仍然多到单台服务器无法负载,才继续水平切分。
比如一个论坛系统的数据库因当前服务器性能无法满足需要进行分库。先垂直切分,按业务逻辑把用户相关数据表比如用户信息、积分、用户间私信等放入user数据库;论坛相关数据表比如板块,帖子,回复等放入forum数据库,两个数据库放在不同服务器上。
拆分后表往往不可能完全无关联,比如帖子中的发帖人、回复人这些信息都在user数据库中。未拆分前可能一次联表查询就能获取当前帖子的回复、发帖人、回复人等所有信息,拆分后因为跨数据库无法联表查询,只能多次查询获得最终数据。
所以总结起来,分库的目的是降低单台服务器负载,切分原则是根据业务紧密程度拆分,缺点是跨数据库无法联表查询。
分表
分表的原因
当数据量超大的时候,B-Tree索引就无法起作用了。除非是索引覆盖查询,否则数据库服务器需要根据索引扫描的结果回表,查询所有符合条件的记录,如果数据量巨大,这将产生大量随机I/O,随之,数据库的响应时间将大到不可接受的程度。另外,索引维护(磁盘空间、I/O操作)的代价也非常高。
垂直分表
原因:
1.根据MySQL索引实现原理及相关优化策略的内容我们知道Innodb主索引叶子节点存储着当前行的所有信息,所以减少字段可使内存加载更多行数据,有利于查询。
2.受限于操作系统中的文件大小限制。
切分原则:
把不常用或业务逻辑不紧密或存储内容比较多的字段分到新的表中可使表存储更多数据。。
水平分表
原因:
1.随着数据量的增大,table行数巨大,查询的效率越来越低。
2.同样受限于操作系统中的文件大小限制,数据量不能无限增加,当到达一定容量时,需要水平切分以降低单表(文件)的大小。
切分原则: 增量区间或散列或其他业务逻辑。
使用哪种切分方法要根据实际业务逻辑判断。
比如对表的访问多是近期产生的新数据,历史数据访问较少,可以考虑根据时间增量把数据按照一定时间段(比如每年)切分。
如果对表的访问较均匀,没有明显的热点区域,则可以考虑用范围(比如每500w一个表)或普通Hash或一致性Hash来切分。
全局主键问题:
原本依赖数据库生成主键(比如自增)的表在拆分后需要自己实现主键的生成,因为一般拆分规则是建立在主键上的,所以在插入新数据时需要确定主键后才能找到存储的表。
实际应用中也已经有了比较成熟的方案。比如对于自增列做主键的表,flickr的全局主键生成方案很好的解决了性能和单点问题,具体实现原理可以参考这个帖子。除此之外,还有类似于uuid的全局主键生成方案,比如达达参考的Instagram的ID生成器。
一致性Hash:
使用一致性Hash切分比普通的Hash切分可扩展性更强,可以实现拆分表的添加和删除。一致性Hash的具体原理可以参考这个帖子,如果拆分后的表存储
在不同服务器节点上,可以跟帖子一样对节点名或ip取Hash;如果拆分后的表存在一个服务器中则可对拆分后的表名取Hash。