分库分表简述

   随着业务不断的增长,一个大型系统在原有简单主从架构基础上,处理海量级的数据就会显得有些吃力;为了缓解单机压力就出现了切分数据库/表的需求,关于如何切分有以下几种定义:

  1. 垂直分表定义:将一个表按照字段分成多表,每个表存储其中一部分字段。
  2. 垂直分库定义:是指按照业务将表进行分类,分布到不同的数据库上面,每个库可以放在不同的服务器上,它的核心理念是专库专用。
    垂直拆分
  3. 水平分表定义:在同一个数据库内,把同一个表的数据按一定规则拆到多个表中。
    水平拆分
  4. 水平分库定义:把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器上。

   有了上述的需求,就应该有相应的技术方案实现,目前市面上比较常用的有Apache的ShardingSphere生态以及与应用解耦的Mycat还有阿里未开源的DRDS;ShardingSphere生态中Sharding-jdbc是目前比较受欢迎的解决方案,就单从开源的ShardingSphere和Mycat相比较而言,前者是以组件的形式嵌套在系统中,后者作为三方依赖需要独立部署,如果单从理论上谈性能SharingSphere嵌套在代码中,无需经过网络直接跟数据库交互性能应该高于Mycat,有关于ShardingSphere实现可以自行去官网查看,阿里的DRDS是基于TDDL改造的,目前没有开源;本司的SDDL也是基于TDDL,重点梳理下TDDL实现原理。

数据库切分实现原理

   试想一下,如果要我们自己做数据库切分我们该怎么做(8个主从对)?我们从能下手的地方一步一步来

1. 从数据库申请8个主从对

Q:如何在一套应用程序中将8个主从对当成一个来用或者说如何做到灵活切换?CRUD怎么实现?

A:在这里我们可以定义一个规则根据这个规则实现数据库切换,因为这是一个找数据库的过程,我们暂且称之为路由规则

2.路由规则实现

Q:我们路由规则到底应该怎么实现?

A:在这里分为三步走:

  1. 当我们跟数据库建立完连接之后,与之交互最多的就是SQL,所以我们不妨试着从SQL字段下手,这里我们迁出“分片维度”这个概念;
  2. 以订单表为例因为实时订单查的最多的就是用户所以我们将user_id这个字段拿出来,并以此将8个主从对分为一个大组,且称之为order_user_group;
  3. 我们将1-8000平均的分给每个库,称之为槽,即0:[0-999] 1:[1000-1999] … 7:[7000-7999],然后将Math.abs(user_id.hashcode()) % 8000,至此我们即可知道这条数据应该去往哪里

3.我们应该怎么执行这条sql呢?库名?表名怎么拿到呢?

A:在这里会涉及到数据库连接信息(ip:port,db_name,auth_info)我们将其分为两个步:

  1. 获取数据库连接信息:通过第二步我们可以通过Math.abs(user_id.hashcode()) % 8000知道SQL路由到哪里,既然都知是哪个库,我们可以将其数据源信息以及连接元信息同步放在到配置中心;这就拿到了数据库连接信息
  2. 获取表信息:如果表做了水平切分,客户端就应该拼接好相应的SQL直接定向发送,这里就涉及到了“改写规则”以及“解析规则”,在第二步我们去拿user_id的时候就应该开始进行SQL解析,在这里的解析我们其实可以不全部都解析,只解析跟分片相关的重要字段即可,然后拿着解析完的SQL再根据算出来的路由,就可以拼接出最终SQL(SQL解析是个复杂且核心的组件,且对效率有着一定的要求)

4.如果公司大数据和商家也需要查询订单数据会不会有啥问题?

A:的确,大数据会拿订单数据做一系列的报表分析,这里会对数据库做整组大量的查询,如果也在order_user_group操作势必会影响到订单系统的操作;同时商户查看订单时也会整组扫描。因此,我们可以试着去分多个组,比如通过bus_id维度分组,为节省资源可以适当减少主从对,专门对商户提供服务;但是后期随着时间推移,订单数据量激增单表数据量过大时,对于功能的迭代和运维都将会有很大的挑战,这时候我们可以考虑将老数据做归档,分库组中只存放热点数据,比如通过ES集群归档,同时给会员、商户和大数据提供相应数据

5.具体实现

A:至于具体实现,如果是分库,那么可以根据第三步的中的数据源配置信息和一个DataSourcePool给dao层动态的注入DataSource;但是涉及到分表就需要介入dao层的SQLParser或者拦截SQL自己实现相应的解析;有兴趣的
可以去看看阿里TDDL的实现源码,SQL解析可以看Sharding-jdbc官网文档,具体实现还是比较复杂