分库分表策略的可实现架构

分库分表是解决mysql水平扩展的主要手段。网上有关策略的讨论不少,主要是hash扩展、按时间扩展、按范围扩展等等。但真正想实施分库分表的朋友们每每以为“策略听来终觉浅,觉知此事要代码”,所以本文的主要目的是给朋友们提供一个可实现架构。java

JDBCTemplate和Hibernate

你们都知道Hibernate是ORM(对象-关系数据库 mapping)意义上的第一个真正的“统治级”产品。JDBCTemplate则是对Spring对jdbc的简单封装,相对于Hibernate,工程师须要本身写sql,而不是像Hibernate那样直接操做对象解决数据库持久化的问题。由于暴露了sql,JDBCTemplate固然也不利于跨数据库(毕竟每一个数据库的实现产品的sql也不竟相同)。但如今大多数互联网企业都倾向于使用JDBCTemplate,而不是Hibernate。我的认为主要缘由就是性能问题,mysql

1,为获取更好性能,每每根据不一样数据库采用特有的优化方式,即便是DAO层所有用Hibernate实现,迁移数据库也不是轻松的工做。sql

2,使用Hibernate处理关联关系每每将大量数据信息加载到业务系统内存,而不是在数据库系统中处理,只是将最终结果返回。这样破坏了生产系统和DB的解耦,致使DB优化困难,以及生产系统的不安全。数据库

3,分库分表对于Hibernate来讲显得比较复杂安全

能够说第三个缘由是主要的。本文会围绕JDBCTemplate来实现分库分表,若是你还在使用Hibernate,建议逐渐切换到JDCBTemplate。session

分库分表策略

分库分表策略,简单来讲就是根据要被持久化的数据,分配一个库或者表来读/写。所以DBSplitStrategy接口定义以下,架构

Java代码 复制代码 收藏代码
  1. interface DBSplitStrategy {
  2. String getDBName(long id); // 获取库名
  3. String getTableSuffix(long id); // 获取表名
  4. JdbcTemplate getIdxJdbcTemplate(long id); // 获取db jt
  5. JdbcTemplate getIdxJdbcTemplate(String dbname); // 根据库名获取 db jt
  6. JdbcTemplate getIdxJdbcTemplateByTable(String table); // 根据表名获取db jt
  7. }
interface DBSplitStrategy {
	String getDBName(long id); // 获取库名
	String getTableSuffix(long id); // 获取表名
	JdbcTemplate getIdxJdbcTemplate(long id); // 获取db jt
	JdbcTemplate getIdxJdbcTemplate(String dbname); // 根据库名获取 db jt
	JdbcTemplate getIdxJdbcTemplateByTable(String table); // 根据表名获取db jt
}

接口定义是围绕最基本的:key -> 逻辑库名/表名 -> 物理库名/表名app

实现类性能



以最多见的HashSplit为例,首先咱们须要几个基本的配置项:(1)基本库名,也能够叫库名前缀;(2)分库总数;(3)分表总数;(4)分库对应的物理地址,即JDBCTemplate定义优化

Spring 配置
<bean id="dataService" class="DBSplitStrategy">
<property name="DBNameBase" value="session_" />
<property name="splitDBCount" value="16" />
<property name="splitTbCount" value="64" />
<property name="dmJts">
<map>
<entry key="session_1" value-ref="jts1"></entry>
<entry key="session_2" value-ref="jts2"></entry>

...

有了以上配置,代码工做只须要把输入的关键词安装策略转换成逻辑库名、表名便可,伪代码以下,

Java代码 复制代码 收藏代码
  1. public String getTableName(long id) {
  2. long hash = getHash4split(id, splitCount);
  3. return tbNameBase + String.valueOf(hash / shareDBCount + 1);
  4. }
  5. public String getDBName(long id) {
  6. long hash = getHash4split(id, splitCount);
  7. return dbNameBase + ( hash % shareDBCount + 1);
  8. }
public String getTableName(long id) {
   long hash = getHash4split(id, splitCount);
   return tbNameBase + String.valueOf(hash / shareDBCount + 1);
}

public String getDBName(long id) {
   long hash = getHash4split(id, splitCount);
   return dbNameBase + ( hash % shareDBCount + 1);
}

这段代码里有个有趣的逻辑,若是你的业务主键从 1 一直增加,那么分库分表的结果就是:库1,表0;库2,表0;库3,表0;..... 库1,表2;库2,表2;...

总结

Mysql分库分表,水平扩展还有不少问题这里没有涉及到,好比,

  1. 若是最初分配的64个分表不够用了怎么办?这是最初决定分库分表是须要考虑的重要问题,由于hash容易,rehash难。
  2. 这么多数据分散在不一样的库表中,怎么分析和挖掘呢?
  3. 怎么样的分库策略更适合你呢?