精华内容
下载资源
问答
  •   ...   一、sharding逻辑的... 从一个系统的程序架构层面来看,sharding逻辑可以在DAO层、JDBC API层、介于DAO与JDBC之间的Spring数据访问封装层(各种spring的template)以及介于应用服务器与数据库之间的sha...

     

    转自:http://blog.csdn.net/bluishglc/article/details/7766508

     

    一、sharding逻辑的实现层面

           从一个系统的程序架构层面来看,sharding逻辑可以在DAO层、JDBC API层、介于DAO与JDBC之间的Spring数据访问封装层(各种spring的template)以及介于应用服务器与数据库之间的sharding代理服务器四个层面上实现。

    图1. Sharding实现层面与相关框架/产品

    (a) 在DAO层实现

           当团队决定自行实现sharding的时候,DAO层可能是嵌入sharding逻辑的首选位置,因为在这个层面上,每一个DAO的方法都明确地知道需要访问的数据表以及查询参数,借助这些信息可以直接定位到目标shard上,而不必像框架那样需要对SQL进行解析然后再依据配置的规则进行路由。另一个优势是不会受ORM框架的制约。由于现在的大多数应用在数据访问层上会依赖某种ORM框架,而多数的shrading框架往往无法支持或只能支持一种orm框架,这使得在选择和应用框架时受到了很大的制约,而自行实现sharding完全没有这方面的问题,甚至不同的shard使用不同的orm框架都可以在一起协调工作。比如现在的java应用大多使用hibernate,但是当下还没有非常令人满意的基于hibernate的sharding框架,(关于hibernate hards会在下文介绍),因此很多团队会选择自行实现sharding。

     

           简单总结一下,在DAO层自行实现sharding的优势在于:不受ORM框架的制约、实现起来较为简单、易于根据系统特点进行灵活的定制、无需SQL解析和路由规则匹配,性能上表现会稍好一些;劣势在于:有一定的技术门槛,工作量比依靠框架实现要大(反过来看,框架会有学习成本)、不通用,只能在特定系统里工作。当然,在DAO层同样可以通过XML配置或是注解将sharding逻辑抽离到“外部”,形成一套通用的框架. 不过目前还没有出现此类的框架。

     

    (b)  在ORM框架层实现

           在ORM框架层实现sharding有两个方向,一个是在实现O-R Mapping的前提下同时提供sharding支持,从而定位为一种分布式的数据访问框架,这一类类型的框架代表就是guzz另一个方向是通过对既有ORM框架进行修改增强来加入sharding机制。此类型的代表产品是hibernate shard. 应该说以hibernate这样主流的地位,行业对于一款面向hibernate的sharding框架的需求是非常迫切的,但是就目前的hibernate shards来看,表现还算不上令人满意,主要是它对使用hibernate的限制过多,比如它对HQL的支持就非常有限。在mybatis方面,目前还没有成熟的相关框架产生。有人提出利用mybatis的插件机制实现sharding,但是遗憾的是,mybatis的插件机制控制不到多数据源的连接层面,另一方面,离开插件层又失去了对sql进行集中解析和路由的机会,因此在mybatis框架上,目前还没有可供借鉴的框架,团队可能要在DAO层或Spring模板类上下功夫了。

     

    (c)  在JDBC API层实现

           JDBC API层是很多人都会想到的一个实现sharding的绝佳场所,如果我们能提供一个实现了sharding逻辑的JDBC API实现,那么sharding对于整个应用程序来说就是完全透明的,而这样的实现可以直接作为通用的sharding产品了。但是这种方案的技术门槛和工作量显然不是一般团队能做得来的,因此基本上没有团队会在这一层面上实现sharding,甚至也没有此类的开源产品。笔者知道的只有一款商业产品dbShards采用的是这一方案。

     

    (d)  在介于DAO与JDBC之间的Spring数据访问封装层实现

    在springd大行其道的今天,几乎没有哪个java平台上构建的应用不使用spring,在DAO与JDBC之间,spring提供了各种template来管理资源的创建与释放以及与事务的同步,大多数基于spring的应用都会使用template类做为数据访问的入口,这给了我们另一个嵌入sharding逻辑的机会,就是通过提供一个嵌入了sharding逻辑的template类来完成sharding工作.这一方案在效果上与基于JDBC API实现的方案基本一致,同样是对上层代码透明,在进行sharding改造时可以平滑地过度,但它的实现却比基于JDBC API的方式简单,因此成为了不少框架的选择,阿里集团研究院开源的Cobar Client就是这类方案的一种实现。

    (e)  在应用服务器与数据库之间通过代理实现

           在应用服务器与数据库之间加入一个代理,应用程序向数据发出的数据请求会先通过代理,代理会根据配置的路由规则,对SQL进行解析后路由到目标shard,因为这种方案对应用程序完全透明,通用性好,所以成为了很多sharding产品的选择。在这方面较为知名的产品是mysql官方的代理工具:Mysql Proxy和一款国人开发的产品:amoeba。mysql proxy本身并没有实现任何sharding逻辑,它只是作为一种面向mysql数据库的代理,给开发人员提供了一个嵌入sharding逻辑的场所,它使用lua作为编程语言,这对很多团队来说是需要考虑的一个问题。amoeba则是专门实现读写分离与sharding的代理产品,它使用非常简单,不使用任何编程语言,只需要通过xml进行配置。不过amoeba不支持事务(从应用程序发出的包含事务信息的请求到达amoeba时,事务信息会被抹去,因此,即使是单点数据访问也不会有事务存在)一直是个硬伤。当然,这要看产品的定位和设计理念,我们只能说对于那些对事务要求非常高的系统,amoeba是不适合的。

    二、使用框架还是自主开发?
           前面的讨论中已经罗列了很多开源框架与产品,这里再整理一下:基于代理方式的有MySQL Proxy和Amoeba,基于Hibernate框架的是Hibernate Shards,通过重写spring的ibatis template类是Cobar Client,这些框架各有各的优势与短板,架构师可以在深入调研之后结合项目的实际情况进行选择,但是总的来说,我个人对于框架的选择是持谨慎态度的。一方面多数框架缺乏成功案例的验证,其成熟性与稳定性值得怀疑。另一方面,一些从成功商业产品开源出框架(如阿里和淘宝的一些开源项目)是否适合你的项目是需要架构师深入调研分析的。当然,最终的选择一定是基于项目特点、团队状况、技术门槛和学习成本等综合因素考量确定的。

     

    展开全文
  •   版权声明:本文由本人撰写并发表于2012年9月份的《程序员》杂志,...作为一种数据存储层面上的水平伸缩解决方案,数据库Sharding技术由来已久,很多海量数据系统在其发展演进的历程中都曾经历过分库分表的Sha...

     

    版权声明:本文由本人撰写并发表于2012年9月份的《程序员》杂志,原文题目《一种支持自由规划的Sharding扩容方案——主打无须数据迁移和修改路由代码》,此处作为本系列的第五篇文章进行转载, 本文版权归《程序员》杂志所有,未经许可不得转载!

     

    作为一种数据存储层面上的水平伸缩解决方案,数据库Sharding技术由来已久,很多海量数据系统在其发展演进的历程中都曾经历过分库分表的Sharding改造阶段。简单地说,Sharding就是将原来单一数据库按照一定的规则进行切分,把数据分散到多台物理机(我们称之为Shard)上存储,从而突破单机限制,使系统能以Scale-Out的方式应对不断上涨的海量数据,但是这种切分对上层应用来说是透明的,多个物理上分布的数据库在逻辑上依然是一个库。实现Sharding需要解决一系列关键的技术问题,这些问题主要包括:切分策略、节点路由、全局主键生成、跨节点排序/分组/表关联、多数据源事务处理和数据库扩容等。关于这些问题可以参考笔者的博客专栏http://blog.csdn.net/column/details/sharding.html 本文将重点围绕“数据库扩容”进行深入讨论,并提出一种允许自由规划并能避免数据迁移和修改路由代码的Sharding扩容方案。

     

    Sharding扩容——系统维护不能承受之重

     

    任何Sharding系统,在上线运行一段时间后,数据就会积累到当前节点规模所能承载的上限,此时就需要对数据库进行扩容了,也就是增加新的物理结点来分摊数据。如果系统使用的是基于ID进行散列的路由方式,那么团队需要根据新的节点规模重新计算所有数据应处的目标Shard,并将其迁移过去,这对团队来说无疑是一个巨大的维护负担;而如果系统是按增量区间进行路由(如每1千万条数据或是每一个月的数据存放在一个节点上 ),虽然可以避免数据的迁移,却有可能带来“热点”问题,也就是近期系统的读写都集中在最新创建的节点上(很多系统都有此类特点:新生数据的读写频率明显高于旧有数据),从而影响了系统性能。面对这种两难的处境,Sharding扩容显得异常困难。

     

    一般来说,“理想”的扩容方案应该努力满足以下几个要求:

    1.  最好不迁移数据 (无论如何,数据迁移都是一个让团队压力山大的问题)
    2. 允许根据硬件资源自由规划扩容规模和节点存储负载
    3. 能均匀的分布数据读写,避免“热点”问题
    4. 保证对已经达到存储上限的节点不再写入数据

     

    目前,能够避免数据迁移的优秀方案并不多,相对可行的有两种,一种是维护一张记录数据ID和目标Shard对应关系的映射表,写入时,数据都写入新扩容的Shard,同时将ID和目标节点写入映射表,读取时,先查映射表,找到目标Shard后再执行查询。该方案简单有效,但是读写数据都需要访问两次数据库,且映射表本身也极易成为性能瓶颈。为此系统不得不引入分布式缓存来缓存映射表数据,但是这样也无法避免在写入时访问两次数据库,同时大量映射数据对缓存资源的消耗以及专门为此而引入分布式缓存的代价都是需要权衡的问题。另一种方案来自淘宝综合业务平台团队,它利用对2的倍数取余具有向前兼容的特性(如对4取余得1的数对2取余也是1)来分配数据,避免了行级别的数据迁移,但是依然需要进行表级别的迁移,同时对扩容规模和分表数量都有限制。总得来说,这些方案都不是十分的理想,多多少少都存在一些缺点,这也从一个侧面反映出了Sharding扩容的难度。

     

    取长补短,兼容并包——一种理想的Sharding扩容方案

     

    如前文所述,Sharding扩容与系统采用的路由规则密切相关:基于散列的路由能均匀地分布数据,但却需要数据迁移,同时也无法避免对达到上限的节点不再写入新数据;基于增量区间的路由天然不存在数据迁移和向某一节点无上限写入数据的问题,但却存在“热点”困扰。我们设计方案的初衷就是希望能结合两种路由规则的优势,摒弃各自的劣势,创造出一种接近“理想”状态的扩容方式,而这种方式简单概括起来就是:全局按增量区间分布数据,使用增量扩容,无数据迁移,局部使用散列方式分散数据读写,解决“热点”问题,同时对Sharding拓扑结构进行建模,使用一致的路由算法,扩容时只需追加节点数据,不再修改散列逻辑代码。

     

    原理

     

    首先,作为方案的基石,为了能使系统感知到Shard并基于Shard的分布进行路由计算,我们需要建立一个可以描述Sharding拓扑结构的编程模型。按照一般的切分原则,一个单一的数据库会首先进行垂直切分,垂直切分只是将关系密切的表划分在一起,我们把这样分出的一组表称为一个Partition。 接下来,如果Partition里的表数据量很大且增速迅猛,就再进行水平切分,水平切分会将一张表的数据按增量区间或散列方式分散到多个Shard上存储。在我们的方案里,我们使用增量区间与散列相结合的方式,全局上,数据按增量区间分布,但是每个增量区间并不是按照某个Shard的存储规模划分的,而是根据一组Shard的存储总量来确定的,我们把这样的一组Shard称为一个ShardGroup,局部上,也就是一个ShardGroup内,记录会再按散列方式均匀分布到组内各Shard上。这样,一条数据的路由会先根据其ID所处的区间确定ShardGroup,然后再通过散列命中ShardGroup内的某个目标Shard。在每次扩容时,我们会引入一组新的Shard,组成一个新的ShardGroup,为其分配增量区间并标记为“可写入”,同时将原有ShardGroup标记为“不可写入”,于是新生数据就会写入新的ShardGroup,旧有数据不需要迁移。同时,在ShardGroup内部各Shard之间使用散列方式分布数据读写,进而又避免了“热点”问题。最后,在Shard内部,当单表数据达到一定上限时,表的读写性能就开始大幅下滑,但是整个数据库并没有达到存储和负载的上限,为了充分发挥服务器的性能,我们通常会新建多张结构一样的表,并在新表上继续写入数据,我们把这样的表称为“分段表”(Fragment Table)。不过,引入分段表后所有的SQL在执行前都需要根据ID将其中的表名替换成真正的分段表名,这无疑增加了实现Sharding的难度,如果系统再使用了某种ORM框架,那么替换起来可能会更加困难。目前很多数据库提供一种与分段表类似的“分区”机制,但没有分段表的副作用,团队可以根据系统的实现情况在分段表和分区机制中灵活选择。总之,基于上述切分原理,我们将得到如下Sharding拓扑结构的领域模型:

     

    图1. Sharding拓扑结构领域模型

     

     

    在这个模型中,有几个细节需要注意:ShardGroup的writable属性用于标识该ShardGroup是否可以写入数据,一个Partition在任何时候只能有一个ShardGroup是可写的,这个ShardGroup往往是最近一次扩容引入的;startId和endId属性用于标识该ShardGroup的ID增量区间;Shard的hashValue属性用于标识该Shard节点接受哪些散列值的数据;FragmentTable的startId和endId是用于标识该分段表储存数据的ID区间。

     

    确立上述模型后,我们需要通过配置文件或是在数据库中建立与之对应的表来存储节点元数据,这样,整个存储系统的拓扑结构就可以被持久化起来,系统启动时就能从配置文件或数据库中加载出当前的Sharding拓扑结构进行路由计算了,扩容时只需要向对应的文件或表中加入相关的节点信息重启系统即可,不需要修改任何路由逻辑代码。

     

    示例

     

    让我们通过示例来了解这套方案是如何工作的。

     

    阶段一:初始上线

     

    假设某系统初始上线,规划为某表提供4000W条记录的存储能力,若单表存储上限为1000W条,单库存储上限为2000W条,共需2个Shard,每个Shard包含两个分段表,ShardGroup增量区间为0-4000W,按2取余分散到2个Shard上,具体规划方案如下:

    图2. 初始4000W存储规模的规划方案

     

    与之相适应,Sharding拓扑结构的元数据如下:

    图3. 对应Sharding元数据

     

     

    阶段二:系统扩容

     

    经过一段时间的运行,当原表总数据逼近4000W条上限时,系统就需要扩容了。为了演示方案的灵活性,我们假设现在有三台服务器Shard2、Shard3、Shard4,其性能和存储能力表现依次为Shard2<Shard3<Shard4,我们安排Shard2储存1000W条记录,Shard3储存2000W条记录,Shard4储存3000W条记录,这样,该表的总存储能力将由扩容前的4000W条提升到10000W条,以下是详细的规划方案:

    图4. 二次扩容6000W存储规模的规划方案

     

    相应拓扑结构表数据下:

     

    图5. 对应Sharding元数据

     

     

    从这个扩容案例中我们可以看出该方案允许根据硬件情况进行灵活规划,对扩容规模和节点数量没有硬性规定,是一种非常自由的扩容方案。

     

    增强

     

    接下来让我们讨论一个高级话题:对“再生”存储空间的利用。对于大多数系统来说,历史数据较为稳定,被更新或是删除的概率并不高,反映到数据库上就是历史Shard的数据量基本保持恒定,但也不排除某些系统其数据有同等的删除概率,甚至是越老的数据被删除的可能性越大,这样反映到数据库上就是历史Shard随着时间的推移,数据量会持续下降,在经历了一段时间后,节点就会腾出很大一部分存储空间,我们把这样的存储空间叫“再生”存储空间,如何有效利用再生存储空间是这些系统在设计扩容方案时需要特别考虑的。回到我们的方案,实际上我们只需要在现有基础上进行一个简单的升级就可以实现对再生存储空间的利用,升级的关键就是将过去ShardGroup和FragmentTable的单一的ID区间提升为多重ID区间。为此我们把ShardGroup和FragmentTable的ID区间属性抽离出来,分别用ShardGroupInterval和FragmentTableIdInterval表示,并和它们保持一对多关系。

     

    图6. 增强后的Sharding拓扑结构领域模型

     

     

    让我们还是通过一个示例来了解升级后的方案是如何工作的。

     

    阶段三:不扩容,重复利用再生存储空间

     

    假设系统又经过一段时间的运行之后,二次扩容的6000W条存储空间即将耗尽,但是由于系统自身的特点,早期的很多数据被删除,Shard0和Shard1又各自腾出了一半的存储空间,于是ShardGroup0总计有2000W条的存储空间可以重新利用。为此,我们重新将ShardGroup0标记为writable=true,并给它追加一段ID区间:10000W-12000W,进而得到如下规划方案:

    图7. 重复利用2000W再生存储空间的规划方案

     

     

    相应拓扑结构的元数据如下:

    图8. 对应Sharding元数据

     

    小结

     

    这套方案综合利用了增量区间和散列两种路由方式的优势,避免了数据迁移和“热点”问题,同时,它对Sharding拓扑结构建模,使用了一致的路由算法,从而避免了扩容时修改路由代码,是一种理想的Sharding扩容方案。

    展开全文
  • 前几天时间写了如何使用Sharding-JDBC进行分库分表和读写分离的例子,相信能够感受到Sharding-JDBC的强大了,而且使用配置都非常干净。官方支持的功能还很多功能分布式主键、强制路由等。这里是最终版介绍下如何在...

    一. 前言

    前几天时间写了如何使用Sharding-JDBC进行分库分表和读写分离的例子,相信能够感受到Sharding-JDBC的强大了,而且使用配置都非常干净。官方支持的功能还很多功能分布式主键、强制路由等。这里是最终版介绍下如何在分库分表的基础上集成读写分离的功能。

    推荐先阅读:
    SpringBoot 2.x ShardingSphere分库分表实战
    SpringBoot 2.x ShardingSphere读写分离实战

    二. 项目实战

    主从数据库配置

    在配置前,我们希望分库分表规则和之前保持一致:

    基于user表,根据id进行分库,如果id mod 2为奇数则落在ds0库,偶数则落在ds1库
    根据age进行分表,如果age mod 2为奇数则落在user_0表,偶数则落在user_1表
    复制代码

    读写分离规则:

    读都落在从库,写落在主库
    复制代码

    因为使用我们使用Sharding-JDBC Spring Boot Starter,所以还是只需要在properties配置文件配置主从库的数据源即可

    # 可以看到配置四个数据源 分别是 主数据库两个 从数据库两个
    sharding.jdbc.datasource.names=master0,master1,master0slave0,master1slave0
    # 主第一个数据库
    sharding.jdbc.datasource.master0.type=com.zaxxer.hikari.HikariDataSource
    sharding.jdbc.datasource.master0.hikari.driver-class-name=com.mysql.jdbc.Driver
    sharding.jdbc.datasource.master0.jdbc-url=jdbc:mysql://192.168.0.4:3306/ds0?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    sharding.jdbc.datasource.master0.username=test
    sharding.jdbc.datasource.master0.password=12root
    # 主第二个数据库
    sharding.jdbc.datasource.master1.type=com.zaxxer.hikari.HikariDataSource
    sharding.jdbc.datasource.master1.hikari.driver-class-name=com.mysql.jdbc.Driver
    sharding.jdbc.datasource.master1.jdbc-url=jdbc:mysql://192.168.0.4:3306/ds1?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    sharding.jdbc.datasource.master1.username=test
    sharding.jdbc.datasource.master1.password=12root
    # 从第一个数据库
    sharding.jdbc.datasource.master0slave0.type=com.zaxxer.hikari.HikariDataSource
    sharding.jdbc.datasource.master0slave0.hikari.driver-class-name=com.mysql.jdbc.Driver
    sharding.jdbc.datasource.master0slave0.jdbc-url=jdbc:mysql://192.168.0.3:3306/ds0?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    sharding.jdbc.datasource.master0slave0.username=test
    sharding.jdbc.datasource.master0slave0.password=12root
    # 从第一个数据库
    sharding.jdbc.datasource.master1slave0.type=com.zaxxer.hikari.HikariDataSource
    sharding.jdbc.datasource.master1slave0.hikari.driver-class-name=com.mysql.jdbc.Driver
    sharding.jdbc.datasource.master1slave0.jdbc-url=jdbc:mysql://192.168.0.3:3306/ds1?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    sharding.jdbc.datasource.master1slave0.username=test
    sharding.jdbc.datasource.master1slave0.password=12root
    
    # 读写分离配置
    # 从库的读取规则为round_robin(轮询策略),除了轮询策略,还有支持random(随机策略)
    sharding.jdbc.config.masterslave.load-balance-algorithm-type=round_robin
    # 逻辑主从库名和实际主从库映射关系
    # 主数据库0
    sharding.jdbc.config.sharding.master-slave-rules.ds0.master-data-source-name=master0
    # 从数据库0
    sharding.jdbc.config.sharding.master-slave-rules.ds0.slave-data-source-names=master0slave0
    # 主数据库1
    sharding.jdbc.config.sharding.master-slave-rules.ds1.master-data-source-name=master1
    # 从数据库1
    sharding.jdbc.config.sharding.master-slave-rules.ds1.slave-data-source-names=master1slave0
    
    
    # 分库分表配置
    # 水平拆分的数据库(表) 配置分库 + 分表策略 行表达式分片策略
    # 分库策略
    sharding.jdbc.config.sharding.default-database-strategy.inline.sharding-column=id
    sharding.jdbc.config.sharding.default-database-strategy.inline.algorithm-expression=ds$->{id % 2}
    # 分表策略 其中user为逻辑表 分表主要取决于age行
    sharding.jdbc.config.sharding.tables.user.actual-data-nodes=ds$->{0..1}.user_$->{0..1}
    sharding.jdbc.config.sharding.tables.user.table-strategy.inline.sharding-column=age
    # 分片算法表达式
    sharding.jdbc.config.sharding.tables.user.table-strategy.inline.algorithm-expression=user_$->{age % 2}
    
    # 主键 UUID 18位数 如果是分布式还要进行一个设置 防止主键重复
    #sharding.jdbc.config.sharding.tables.user.key-generator-column-name=id
    
    # 打印操作的sql以及库表数据等
    sharding.jdbc.config.props.sql.show=true
    spring.main.allow-bean-definition-overriding=true
    复制代码

    其他项目配置不变,和之前保持一致即可

    三. 测试

    1.查询全部数据库

    打开浏览器输入 http://localhost:8080/select

    控制台打印

    2.插入数据

    打开浏览器 分别访问

    http://localhost:8080/insert?id=1&name=lhd&age=12
    http://localhost:8080/insert?id=2&name=lhd&age=13
    http://localhost:8080/insert?id=3&name=lhd&age=14
    http://localhost:8080/insert?id=4&name=lhd&age=15
    复制代码

    控制台打印

    结果和之前的一样 根据分片算法和分片策略,不同的id以及age取模落入不同的库表 达到了分库分表

    3.查询全部数据

    打开浏览器输入 http://localhost:8080/select

    控制台打印

    四. 问题

    1. 无法知道走的到底是哪个数据源
    相信大家也发现了,当读写分离和分库分表集成时
    虽然我们配置sql.show=true
    但是控制台最终打印不出所执行的数据源是哪个
    不知道是从库还是主库
    复制代码
    2.读写分离实现

    读写分离的流程

    获取主从库配置规则,数据源封装成MasterSlaveDataSource
    根据ShardingMasterSlaveRouter路由计算,得到sqlRouteResult.getRouteUnits()单元列表,然后将结果addAll添加并返回
    执行每个RouteUnits的时候需要获取连接,这里根据轮询负载均衡算法RoundRobinMasterSlaveLoadBalanceAlgorithm得到从库数据源,拿到连接后就开始执行具体的SQL查询了,这里通过PreparedStatementHandler.execute()得到执行结果
    结果归并后返回
    复制代码

    MasterSlaveDataSource.class

    package io.shardingsphere.shardingjdbc.jdbc.core.datasource;
    
    import io.shardingsphere.api.ConfigMapContext;
    import io.shardingsphere.api.config.rule.MasterSlaveRuleConfiguration;
    import io.shardingsphere.core.constant.properties.ShardingProperties;
    import io.shardingsphere.core.rule.MasterSlaveRule;
    import io.shardingsphere.shardingjdbc.jdbc.adapter.AbstractDataSourceAdapter;
    import io.shardingsphere.shardingjdbc.jdbc.core.connection.MasterSlaveConnection;
    import io.shardingsphere.transaction.api.TransactionTypeHolder;
    import java.sql.Connection;
    import java.sql.DatabaseMetaData;
    import java.sql.SQLException;
    import java.util.Map;
    import java.util.Properties;
    import javax.sql.DataSource;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class MasterSlaveDataSource extends AbstractDataSourceAdapter {
        private static final Logger log = LoggerFactory.getLogger(MasterSlaveDataSource.class);
        private final DatabaseMetaData databaseMetaData;
        private final MasterSlaveRule masterSlaveRule;
        private final ShardingProperties shardingProperties;
    
        public MasterSlaveDataSource(Map<String, DataSource> dataSourceMap, MasterSlaveRuleConfiguration masterSlaveRuleConfig, Map<String, Object> configMap, Properties props) throws SQLException {
            super(dataSourceMap);
            this.databaseMetaData = this.getDatabaseMetaData(dataSourceMap);
            if (!configMap.isEmpty()) {
                ConfigMapContext.getInstance().getConfigMap().putAll(configMap);
            }
    
            this.masterSlaveRule = new MasterSlaveRule(masterSlaveRuleConfig);
            // 从配置文件获取配置的主从数据源
            this.shardingProperties = new ShardingProperties(null == props ? new Properties() : props);
        }
    
        // 获取主从配置关系
        public MasterSlaveDataSource(Map<String, DataSource> dataSourceMap, MasterSlaveRule masterSlaveRule, Map<String, Object> configMap, Properties props) throws SQLException {
            super(dataSourceMap);
            this.databaseMetaData = this.getDatabaseMetaData(dataSourceMap);
            if (!configMap.isEmpty()) {
                ConfigMapContext.getInstance().getConfigMap().putAll(configMap);
            }
    
            this.masterSlaveRule = masterSlaveRule;
            this.shardingProperties = new ShardingProperties(null == props ? new Properties() : props);
        }
    
        // 获取数据库元数据
        private DatabaseMetaData getDatabaseMetaData(Map<String, DataSource> dataSourceMap) throws SQLException {
            Connection connection = ((DataSource)dataSourceMap.values().iterator().next()).getConnection();
            Throwable var3 = null;
    
            DatabaseMetaData var4;
            try {
                var4 = connection.getMetaData();
            } catch (Throwable var13) {
                var3 = var13;
                throw var13;
            } finally {
                if (connection != null) {
                    if (var3 != null) {
                        try {
                            connection.close();
                        } catch (Throwable var12) {
                            var3.addSuppressed(var12);
                        }
                    } else {
                        connection.close();
                    }
                }
    
            }
    
            return var4;
        }
    
        public final MasterSlaveConnection getConnection() {
            return new MasterSlaveConnection(this, this.getShardingTransactionalDataSources().getDataSourceMap(), TransactionTypeHolder.get());
        }
    
        public DatabaseMetaData getDatabaseMetaData() {
            return this.databaseMetaData;
        }
    
        public MasterSlaveRule getMasterSlaveRule() {
            return this.masterSlaveRule;
        }
    
        public ShardingProperties getShardingProperties() {
            return this.shardingProperties;
        }
    }
    
    复制代码

    配置文件配置的主从规则
    MasterSlaveRule.class

    package io.shardingsphere.core.rule;
    
    import com.google.common.base.Preconditions;
    import io.shardingsphere.api.algorithm.masterslave.MasterSlaveLoadBalanceAlgorithm;
    import io.shardingsphere.api.algorithm.masterslave.MasterSlaveLoadBalanceAlgorithmType;
    import io.shardingsphere.api.config.rule.MasterSlaveRuleConfiguration;
    import java.util.Collection;
    
    public class MasterSlaveRule {
        //名称(这里是ds0和ds1)
        private final String name;
        //主库数据源名称(这里是ds_master_0和ds_master_1)
        private final String masterDataSourceName;
        //所属从库列表,key为从库数据源名称,value是真实的数据源
        private final Collection<String> slaveDataSourceNames;
        //主从库负载均衡算法
        private final MasterSlaveLoadBalanceAlgorithm loadBalanceAlgorithm;
        //主从库路由配置
        private final MasterSlaveRuleConfiguration masterSlaveRuleConfiguration;
    
    复制代码

    轮询负载均衡算算法 RoundRobinMasterSlaveLoadBalanceAlgorithm.class

    package io.shardingsphere.api.algorithm.masterslave;
    
    import java.util.List;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.atomic.AtomicInteger;
    
    //轮询负载均衡策略,按照每个从节点访问次数均衡
    public final class RoundRobinMasterSlaveLoadBalanceAlgorithm implements MasterSlaveLoadBalanceAlgorithm {
        private static final ConcurrentHashMap<String, AtomicInteger> COUNT_MAP = new ConcurrentHashMap();
    
        public RoundRobinMasterSlaveLoadBalanceAlgorithm() {
        }
    
        public String getDataSource(String name, String masterDataSourceName, List<String> slaveDataSourceNames) {
            AtomicInteger count = COUNT_MAP.containsKey(name) ? (AtomicInteger)COUNT_MAP.get(name) : new AtomicInteger(0);
            COUNT_MAP.putIfAbsent(name, count);
            count.compareAndSet(slaveDataSourceNames.size(), 0);
            return (String)slaveDataSourceNames.get(Math.abs(count.getAndIncrement()) % slaveDataSourceNames.size());
        }
    }
    
    复制代码

    ShardingMasterSlaveRouter.class

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package io.shardingsphere.core.routing.router.masterslave;
    
    import io.shardingsphere.core.constant.SQLType;
    import io.shardingsphere.core.hint.HintManagerHolder;
    import io.shardingsphere.core.routing.RouteUnit;
    import io.shardingsphere.core.routing.SQLRouteResult;
    import io.shardingsphere.core.rule.MasterSlaveRule;
    import java.beans.ConstructorProperties;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    import java.util.LinkedList;
    
    public final class ShardingMasterSlaveRouter {
        private final Collection<MasterSlaveRule> masterSlaveRules;
        
        // 得到最终的sql路由
        public SQLRouteResult route(SQLRouteResult sqlRouteResult) {
            Iterator var2 = this.masterSlaveRules.iterator();
    
            while(var2.hasNext()) {
                MasterSlaveRule each = (MasterSlaveRule)var2.next();
                this.route(each, sqlRouteResult);
            }
    
            return sqlRouteResult;
        }
    
        //进行计算筛选得到最终sql路由
        private void route(MasterSlaveRule masterSlaveRule, SQLRouteResult sqlRouteResult) {
            Collection<RouteUnit> toBeRemoved = new LinkedList();
            Collection<RouteUnit> toBeAdded = new LinkedList();
            Iterator var5 = sqlRouteResult.getRouteUnits().iterator();
    
            while(var5.hasNext()) {
                RouteUnit each = (RouteUnit)var5.next();
                if (masterSlaveRule.getName().equalsIgnoreCase(each.getDataSourceName())) {
                    toBeRemoved.add(each);
                    if (this.isMasterRoute(sqlRouteResult.getSqlStatement().getType())) {
                        MasterVisitedManager.setMasterVisited();
                        toBeAdded.add(new RouteUnit(masterSlaveRule.getMasterDataSourceName(), each.getSqlUnit()));
                    } else {
                        toBeAdded.add(new RouteUnit(masterSlaveRule.getLoadBalanceAlgorithm().getDataSource(masterSlaveRule.getName(), masterSlaveRule.getMasterDataSourceName(), new ArrayList(masterSlaveRule.getSlaveDataSourceNames())), each.getSqlUnit()));
                    }
                }
            }
            //路由移除(查询时 移除所有主库)
            sqlRouteResult.getRouteUnits().removeAll(toBeRemoved);
            //添加从库/主库 具体事件定
            sqlRouteResult.getRouteUnits().addAll(toBeAdded);
        }
    
        // 判断是不是主库
        private boolean isMasterRoute(SQLType sqlType) {
            return SQLType.DQL != sqlType || MasterVisitedManager.isMasterVisited() || HintManagerHolder.isMasterRouteOnly();
        }
    
        @ConstructorProperties({"masterSlaveRules"})
        public ShardingMasterSlaveRouter(Collection<MasterSlaveRule> masterSlaveRules) {
            this.masterSlaveRules = masterSlaveRules;
        }
    }
    
    复制代码

    注: 判断是不是主库的规则为:

    private boolean isMasterRoute(SQLType sqlType) {
            return SQLType.DQL != sqlType || MasterVisitedManager.isMasterVisited() || HintManagerHolder.isMasterRouteOnly();
        }
    复制代码

    SQL语言的判断

    SQL语言共分为四大类:数据查询语言DQL,数据操纵语言DML,数据定义语言DDL,数据控制语言DCL。
    
    复制代码

    通过断点,查询全部数据时最终的sql路由为

    走的从库的四个从表 前面的问题也就迎刃而解

    目前读写分离和分库分表就完成

    源码分析不对,如有错误请指点一二

    源码下载: github.com/LiHaodong88…

    转载于:https://juejin.im/post/5cf4f07ef265da1bd522b974

    展开全文
  • 数据库的基本概念 基本概念这一块,主要是让大家就一些数据库方面的概念达成一致。...接下来是“片”,数据库片是解决数据量大的问题。如果数据量非常大,就要做水平切分,有一些数据库支持auto sha

    http://geek.csdn.net/news/detail/52070

    数据库的基本概念

    基本概念这一块,主要是让大家就一些数据库方面的概念达成一致。

    图片描述

    首先是“单库”,最初的时候数据库都是这么玩的,几乎所有的业务都有这样的一个库。

    图片描述

    接下来是“分片”,数据库的分片是解决数据量大的问题。如果数据量非常大,就要做水平切分,有一些数据库支持auto sharding。之前58同城也用过两年mongoDB,后来发现auto sharding功能不太可控,不知道什么时间进行迁移数据,数据迁移过程中会有大粒度的锁,读写被阻塞,业务会有抖动和毛刺,这些是业务不能接受的,因此现在又迁移回了MySQL

    一旦进行分片,就会面临“数据路由”的问题:来了一个请求,要将请求路由到对应的数据库分片上。互联网常用的数据路由方法有三种:

    (1)第一个是按照数据范围路由,比如有两个分片,一个范围是0-1亿,一个范围是1亿-2亿,这样来路由。 
    这个方式的优点是非常的简单,并且扩展性好,假如两个分片不够了,增加一个2亿-3亿的分片即可。 
    这个方式的缺点是:虽然数据的分布是均衡的,每一个库的数据量差不多,但请求的负载会不均衡。例如有一些业务场景,新注册的用户活跃度更高,大范围的分片请求负载会更高。

    (2)第二个是按照hash路由,比如有两个分片,数据模2寻库即可。 
    这个方式的优点是路由方式很简单,数据分布也是均衡的,请求负载也是均衡的。 
    这个方式的缺点是如果两个分片数据量过大,要变成三个分片,数据迁移会比较麻烦,即扩展性会受限。

    (3)第三个是路由服务。前面两个数据路由方法均有一个缺点,业务线需要耦合路由规则,如果路由规则发生变化,业务线是需要配合升级的。路由服务可以实现业务线与路由规则的解耦,业务线每次访问数据库之前先调用路由服务,来知道数据究竟存放在哪个分库上。

    图片描述

    接下来是“分组”“复制”,这解决的是扩展读性能,读高可用的问题。

    根据经验,大部分互联网的业务都是读多写少。淘宝、京东查询商品,搜索商品的请求可能占了99%,只有下单和支付的时候有写请求。

    58同城搜索帖子,察看列表页,查看详情页都是读请求,发布帖子是写请求,写请求的量也是比较少的。

    可见,大部分互联网的场景都读多写少,所以读性能会最先成为瓶颈,怎么快速解决这个问题呢?

    通常来说,会使用读写分离,扩充读库的方式来提升读性能。同时也保证了读可用性,一台读库挂了,另外一台读库可以持续的提供服务。

    图片描述

    常见数据库的玩法综合了“分片”和“分组”,数据量大进行分片,为了提高读性能,保证读的高可用,进行了分组,80%互联网公司数据库都是这种软件架构

    可用性架构实践

    数据库大家都用,平时除了根据业务设计表结构,根据访问来设计索引之外,还应该在设计时考虑数据的可用性,可用性又分为读的高可用写的高可用

    图片描述

    上图是“读”高可用的常见玩法。怎么样保证数据库读库的高可用呢?解决高可用这个问题的思路是冗余。

    解决站点的可用性问题冗余多个站点,解决服务的可用性问题冗余多个服务,解决数据的可用性问题冗余多份数据。

    如果用一个读库,保证不了读高可用,就复制读库,一个读库挂了另一个仍然可以提供服务,这么用复制的方式来保证读的可用性。

    数据的冗余会引发一个副作用,就是一致性的问题。

    如果是单库,读和写都落在同一个库上,每次读到的都是最新的数据库,不存在一致性的问题。

    但是为了保证可用性将数据复制到多个地方,而这多个地方的数据绝对不是实时同步的,会有同步时延,所以有可能会读到旧的数据。如何解决主从数据库一致性问题后面再来阐述。

    很多互联网公司的数据库软件架构都是一主两从或者一主三从,不能够保证“写”的高可用,因为写其实还是只有一个库,仍是单点,如果这个库挂了的话,写会受影响。那小伙伴们为什么还使用这个架构呢?

    刚才提到大部分互联网公司99%的业务都是“读”业务,写库不是主要矛盾,写库挂了,可能只有1%的用户会受影响。

    如果要做到“写”的高可用,对数据库软件架构的冲击比较大,不一定值得,为了解决1%的问题引入了80%的复杂度,所以很多互联网公司都没有解决写数据库的高可用的问题。

    图片描述

    怎么来解决这个问题呢?思路还是冗余,读的高可用是冗余读库,写的高可用是冗余写库。把一个写变成两个写,做一个双主同步,一个挂了的话,我可以将写的流量自动切到另外一个,写库的高可用性。

    用双主同步的方式保证写高可用性会存在什么样的问题?

    上文提到,用冗余的方式解保证可用性会存在一致性问题。因为两个主相互同步,这个同步是有时延的,很多公司用到auto-increment-id这样的一些数据库的特性,如果用双组同步的架构,一个主id由10变成11,在没有同步过去之前,另一个主又来了一个写请求,也由10变成11,双向同步会同步失败,就会有数据丢失。

    解决这个双主同步id冲突的方案有两种:

    (1)一个是双主使用不同的初始值,相同的步长来生成id,一个库从0开始(生成02468),一个库从1开始(生成13579),步长都为2,这样两边同步数据就不会冲突。 
    (2)另一个方式是不要使用数据库的auto-increment-id,而由业务层来保证生成的id不冲突。

    图片描述

    58同城没有使用上述两种方式来保证读写的可用性。58同城使用的“双主”当“主从”的方式来保证数据库的读写可用性。

    虽然看上去是双主同步,但是读写都在一个主上,另一个主库没有读写流量,完全standby。当一个主库挂掉的时候,流量会自动的切换到另外一个主库上,这一切对业务线都是透明的,自动完成。

    58同城的这种方案,读写都在一个主库上,就不存同步延时而引发的一致性问题了,但缺点有两个:

    第一是数据库资源的利用率只有50%; 
    第二是没有办法通过增加读库的方式来扩展系统的读性能。 
    58同城的数据库软件架构如何来扩展读性能呢,来看下面一章。

    读性能架构实践

    如何增加数据库的读性能,先看下传统的玩法:

    图片描述

    (1)第一种玩法是增加从库,通过增加从库来提升读性能,存在的问题是什么呢?从库越多,写的性能越慢,同步的时间越长,不一致的可能性越高。

    图片描述

    (2)第二种常见的玩法是增加缓存,缓存是大家用的非常多的一种提高系统读性能的一种方法,特别是对于读多写少的互联网的场景非常的有效。常用的缓存玩法如上图,上游是业务线的,底下是读写分离主从同步,然后会加一个cache。

    对于写操作:会先淘汰cache,再写数据库。

    对于读操作:先读cache,如果cache hit则返回数据,如果cachemiss则读从库,然后把读出来的数据再入缓存。

    这是常见的cache玩法。

    传统的cache玩法在一种异常时序下,会引发严重的一致性问题,考虑这样一个特殊的时序:

    (1)先来了一个写请求,淘汰了cache,写了数据库; 
    (2)又来了一个读请求,读cache,cache miss了,然后读从库,此时写请求还没有同步到从库上,于是读了一个脏数据,接着脏数据入缓存; 
    (3)最后主从同步完成;

    这个时序会导致脏数据一直在缓存中没有办法被淘汰掉,数据库和缓存中的数据严重不一致。

    58同城也是采用缓存的方式来提升读性能的,那就会不会有数据一致性问题呢,请往下看。

    一致性架构实践

    图片描述

    58同城采用“服务+缓存+数据库”一套的方式来保证数据的一致性,由于58同城使用“双主当主从用”的数据库读写高可用架构,读写都在一个主库上,不会读到所谓“读库的脏数据”,所以数据库与缓存的不一致情况也不会存在。

    传统玩法中,主从不一致的问题有一些什么样的解决方案呢?一起来看一下。

    图片描述

    主从为什么会不一致?上文提到读写会有时延,有可能读到从库上的旧数据。常见的方法是引入中间件,业务层不直接访问数据库,而是通过中间件访问数据库,这个中间件会记录哪一些key上发生了写请求,在数据主从同步时间窗口之内,如果key上又出了读请求,就将这个请求也路由到主库上去(因为此时从库可能还没有同步完成,是旧数据),使用这个方法来保证数据的一致性。

    中间件的方案很理想,那为什么大部分的互联网的公司都没有使用这种方案来保证主从数据的一致性呢?那是因为数据库中间件的技术门槛比较高,有一些大公司,例如百度,腾讯,阿里他们可能有自己的中间件,并不是所有的创业公司互联网公司有自己的中间件产品,况且很多互联网公司的业务对数据一致性的要求并没有那么高。比如说同城搜一个帖子,可能5秒钟之后才搜出来,对用户的体验并没有多大的影响。

    图片描述

    除了中间件,读写都路由到主库,58同城就是这么做的,也是一种解决主从不一致的常用方案。

    解决完主从不一致,第二个要解决的是数据库和缓存的不一致,刚才提到cache传统的玩法,脏数据有可能入cache,怎么解决呢?

    两个实践:第一个是缓存双淘汰机制,第二个是建议为所有item设定过期时间(前提是允许cache miss)。

    (1)缓存双淘汰,传统的玩法在进行写操作的时候,先淘汰cache再写主库。上文提到,在主从同步时间窗口之内可能有脏数据入cache,此时如果再发起一个异步的淘汰,即使不一致时间窗内脏数据入了cache,也会再次淘汰掉。 
    (2)为所有item设定超时时间,例如10分钟。极限时序下,即使有脏数据入cache,这个脏数据也最多存在十分钟。带来的副作用是,可能每十分钟,这个key上有一个读请求会穿透到数据库上,但我们认为这对数据库的从库压力增加是非常小的。

    扩展性架构实践

    扩展性也是架构师在做数据库架构设计的时候需要考虑的一点。首先分享一个58同城非常帅气的秒级数据扩容的方案。这个方案解决什么问题呢?原来数据库水平切分成N个库,现在要扩容成2N个库,解决的就是这个问题。

    图片描述

    假设原来分成两个库,假设按照hash的方式分片。如上图,分为奇数库和偶数库。

    图片描述

    第一个步骤提升从库,底下一个从库放到上面来(其实什么动作都没有做); 
    第二个步骤修改配置,此时扩容完成,原来是2个分片,修改配置后变成4个分片,这个过程没有数据的迁移。原来偶数的那一部分现在变成了两个部分,一部分是0,一部分是2,奇数的部分现在变成1和3。0库和2库没有数据冲突,只是扩容之后在短时间内双主的可用性这个特性丢失掉了。

    图片描述

    第三个步骤还要做一些收尾操作:把旧的双主给解除掉,为了保证可用性增加新的双主同步,原来拥有全部的数据,现在只为一半的数据提供服务了,我们把多余的数据删除掉,结尾这三个步骤可以事后慢慢操作。整个扩容在过程在第二步提升从库,修改配置其实就秒级完成了,非常的帅气。

    这个方案的缺点是只能实现N库到2N 库的扩容,2变4、4变8,不能实现2库变3库,2库变5库的扩容。那么,如何能够实现这种扩容呢?

    数据库扩展性方面有很多的需求,例如刚才说的2库扩3库,2库扩5库。产品经理经常变化需求,扩充表的属性也是经常的事情。今年的数据库大会同行也介绍了一些使用触发器来做online schema change的方案,但是触发器的局限性在于:

    第一、触发器对数据库性能的影响比较大; 
    第二、触发器只能在同一个库上才有效,而互联网的场景特点是数据量非常大,并发量非常大,库都分布在不同的物理机器上,触发器没法弄。

    最后还有一类扩展性需求,底层存储介质发生变化,原来是MongoDB存储,现在要变为MySQL存储,这也是扩展性需求(虽然很少),这三类需求怎么扩展?

    方法是导库,迁移数据,迁移数据有几种做法,第一种停服务,如果大家的业务能够接受这种方法,强烈建议使用这种方法,例如有一些游戏公司,晚上一点到两点服务器维护,可能就是在干分区或者合区这类导库的事情。

    图片描述

    如果业务上不允许停服务,想做到平滑迁移,双写法可以解决这类问题。

    (1)双写法迁移数据的第一步是升级服务,原来的服务是写一个库,现在建立新的数据库,双写。比如底层存储介质的变化,我们原来是mongo数据库,现在建立好新的mysql数据库,然后对服务的所有写接口进行双库写升级。

    (2)第二步写一个小程序去进行数据的迁移。比如写一个离线的程序,把两个库的数据重新分片,分到三个库里。也可能是把一个只有三个属性的用户表导到五个属性的数据表里面。这个数据迁移要限速,导完之后两个库的数据一致吗?只要提前双写,如果没有什么意外,两边的数据应该是一致的。

    什么时候会有意外呢?在导某一条数据的过程当中正好发生了一个删除操作,这个数据刚被服务双写删除,又被迁移数据的程序插入到了新库中,这种非常极限的情况下会造成两边的数据不一致。

    (3)建议第三步再开发一个小脚本,对两边的数据进行比对,如果发现了不一致,就将数据修复。当修复完成之后,我们认为数据是一致的,再将双写又变成单写,数据完成迁移。

    这个方式的优点:

    第一、改动是非常小的,对服务的影响比较小,单写变双写,开发两个小工具,一个是迁移程序,从一个库读数据,另外一个库插进去;还有一个数据校验程序,两个数据进行比对,改动是比较小的。

    第二、随时可回滚的,方案风险比较小,在任何一个步骤如果发现问题,可以随时停止操作。比如迁移数据的过程当中发现不对,就把新的数据库干掉,重新再迁。因为在切换之前,所有线上的读服务和写服务都是旧库提供,只有切了以后,才是新库提供的服务。这是我们非常帅气的一个平滑导库的方式。

    总结

    本次分享首先介绍了单库、分片、复制、分组、路由规则的概念。分片解决的是数据量大的问题,复制和分组解决的是提高读性能,保证读的可用性的问题。分片会引入路由,常用的三种路由的方法,按照范围、按照hash,或者新增服务来路由。

    如何保证数据的可用性?思路是冗余,但会引发数据的不一致,58同城保证可用性的实践是双主当主从用,读写流量都在一个库上,另一个库standby,一个主库挂掉流量自动迁移到另外一个主库,只是资源利用率是50%,并且不能通过增加从库的方式提高读性。

    读性能的实践,传统的玩法是增加从库或者增加缓存。存在的问题是,主从可能不一致,同城的玩法是服务加数据库加缓存一套的方式来解决这些问题。

    一致性的实践,解决主从不一致性有两种方法,一种是增加中间件,中间件记录哪些key上发生了写操作,在主从同步时间窗口之内的读操作也路由到主库。第二种方法是强制读主。数据库与缓存的一致性,我们的实践是双淘汰,在发生写请求的时候,淘汰缓存,写入数据库,再做一个延时的缓存淘汰操作。第二个实践是建议为所有的item设置一个超时时间。

    扩展性方面,今天分享了58同城一个非常帅气的N库扩2N库的秒级扩容方案,还分享了一个平滑双写导库的方案,解决两库扩三库,数据库字段的增加,以及底层介质的变化的问题。

    分享人:沈剑,58到家技术总监、58同城高级架构师,58同城技术委员会负责人。曾任百度高级工程师、曾参与过多个百度hi重大项目的研发,加盟58同城以后,负责过58同城即时通讯,支付系统与摊销系统的重构。还曾参与数据库中间件、58同城推荐系统、58同城商户平台App以及58同城二手交易平台APP等多个系统与项目的设计与实现。
    展开全文
  • 分库分表架构实践(文末送书)

    千次阅读 2017-12-18 00:00:00
    作者介绍:丁浪,现就职于某垂直电商平台,担任技术架构师。关注高并发、高可用的架构设计,对系统服务化、分库分表、性能调优等...在谈论数据库架构和数据库优化的时候,我们经常会听到“分库分表”、“分片”、“Sha
  • 在互联网行业海量数据和高并发访问的考验下,聪明的技术人员提出了分库分表技术(有些地方也称为Sharding、分片)。同时,流行的分布式系统中间件(例如MongoDB、ElasticSearch等)均自身友好支持Sha...
  • 一 简介 Apache ShardingSphere是一款开源的分布式数据库中间件组成的生态圈二 成员包含 Sharding-JDBC是一款轻量级的Java框架,在JDBC层提供上述核心功能,使用方式与正常的JDBC方式如出一辙,面向Java开发的用户。...
  • 本文主要将业界知名的开源分库分表中间件—ShardingJdbc集成至SpringBoot工程中,利用ShardingJdbc的数据库切分能力来实现库表水平切分和扩展的目标,提高分布式系统整体的并发量,解决数据库中的单表因数据量过大而...
  • 去年开发一个项目的时候,因为系统的核心数据是定时从外界发送过来的,数据量比较大,后来很快单表就达到了千万级别,这就需要分库分表,最后选择了ShardingSphere,原因就是比较容易上手。 2. Sharding JDBC简介 ...
  • 项目需求在之前我做项目的时候,数据量比较大,单表千万级别的,需要分库分表,于是在网上搜索这方面的开源框架,最常见的就是mycat,sharding-sphere,最终我选择后者,用它来做分库分表比较容易上手。二. 简介sharding-...
  • 由于在做考勤系统考虑到后期考勤数据量会非常大,到时候单表的数据量可能会很大,导致数据库的查询效率会很低,数据库的负载坚持不住,所以就开始考虑分库分表的事情了,一开始是看的比较主流的当初阿里图队的mycat...
  • sharding-jdbc使用

    千次阅读 2016-04-14 19:03:39
    Sharding-JDBC是当当应用框架ddframe中,关系型数据库模块dd-rdb中分离出来的数据库水平扩展框架,即透明化数据库分库分表访问。 Sharding-JDBC继dubbox和elastic-job之后,是ddframe系列开源的第三个产品。 ...
  • 1.简介mycat是一个开源的数据库中间件,其前身是阿里巴巴的cobar,主要能够实现的功能有读写分离,数据分片(垂直分库,垂直分表,水平分库,水平分表)及多数据源整合。其基本原理主要是拦截要执行的sql语句,该sql的...
  • 分布式架构策略思考

    2012-01-29 18:35:26
    一直在了解互联网的大数据高并发的架构,以及所采用的各种优化策略。...垂直切分(Vertical Partition/Sharding):就是把不同格式的数据,存储到不同的数据库,也叫分库 水平切分(Horizontal Partition/Sha...
  • 为了降低数据库被托库导致用户密码风险,用户密码相关信息与用户信息分表(甚至分库)放置,以下为用户账号密码加密设计。 1.用户信息表 列名 说明 备注 用户身份 用户id 用户唯一标志 其他 用户其他信息 不包含密码...
  • 一、什么是Sharding-JDBC Sharding-JDBC定位为轻量级Java框架,在Java的JDBC层提供的额外服务。它使用客户端直连数据库,以jar包形式...分库 & 分表 读写分离 分布式主键 分布式事务 三、适用项目框架 Sha...
  • 本文来自 CSDN 《程序员》 2017 年 2 月的封面报道。 对于一个从零开始的数据库来说:选择什么语言,整体架构怎么做,要不要开源,...在大约两年前,我有一次做 MySQL 分库分表和中间件的经历,那时在中间件里做 sha...
  • awesome-cpp 就是 fffaraz 发起维护的 C++ 资源列表,内容包括:标准、Web应用框架、人工智能、数据库、图片处理、机器学习、日志、代码分析等。 中文版由开源前哨和CPP开发者微信公号团队维护更新,在 GitHub 已...
  • 数据库:Microsoft SQL Server 2008 .NET版本:.NET4.6 【安装教程】 http://www.weisha100.net/course.ashx?id=186【注册后免费学习】 注意事项【解决学员登录后记录不住登录状态问题】 系统安装后用记事本打开db...
  • Golang工作笔记.zip

    2020-06-29 21:02:04
    [第一例 留言板][第二例 gRPC使用例子][第三例 基于go-micro做服务注册和服务发现][第四例 聊天室][第五例 工具][第六例 原生sql操作][第七例 sqlx操作][第八例 Redis数据库(gomodule/redigo)][第九例 Redis消息...
  • java开源包1

    千次下载 热门讨论 2013-06-28 09:14:34
    用来计算 MD5、SHA 哈希算法的 Java 类库,支持 "MD5", "SHA", "SHA-1", "SHA-256", "SHA-384", "SHA-512". 高性能RPC框架 nfs-rpc nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用...
  • java开源包12

    热门讨论 2013-06-28 10:14:45
    用来计算 MD5、SHA 哈希算法的 Java 类库,支持 "MD5", "SHA", "SHA-1", "SHA-256", "SHA-384", "SHA-512". 高性能RPC框架 nfs-rpc nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用...
  • Java资源包01

    2016-08-31 09:16:25
    用来计算 MD5、SHA 哈希算法的 Java 类库,支持 "MD5", "SHA", "SHA-1", "SHA-256", "SHA-384", "SHA-512". 高性能RPC框架 nfs-rpc nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用...
  • java开源包101

    2016-07-13 10:11:08
    用来计算 MD5、SHA 哈希算法的 Java 类库,支持 "MD5", "SHA", "SHA-1", "SHA-256", "SHA-384", "SHA-512". 高性能RPC框架 nfs-rpc nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用...
  • java开源包11

    热门讨论 2013-06-28 10:10:38
    用来计算 MD5、SHA 哈希算法的 Java 类库,支持 "MD5", "SHA", "SHA-1", "SHA-256", "SHA-384", "SHA-512". 高性能RPC框架 nfs-rpc nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用...
  • java开源包2

    热门讨论 2013-06-28 09:17:39
    用来计算 MD5、SHA 哈希算法的 Java 类库,支持 "MD5", "SHA", "SHA-1", "SHA-256", "SHA-384", "SHA-512". 高性能RPC框架 nfs-rpc nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用...

空空如也

空空如也

1 2 3
收藏数 59
精华内容 23
关键字:

数据库分库sha