热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:mysql分库分表方案之shardingjdbc使用(非demo示例)

选择开源核心组件的一个非常重要的考虑通常是社区活跃性,一旦项目团队无法进行自己后续维护和扩展的情况下更是如此。关于分库分表和读写分离、主从一

选择开源核心组件的一个非常重要的考虑通常是社区活跃性,一旦项目团队无法进行自己后续维护和扩展的情况下更是如此。

关于分库分表和读写分离、主从

一般来说,需要分库分表的系统是流量比较大的,而且比较容易出现峰值的比如说打折/活动的时候;其次,当单机扛不住业务流量的时候,分库分表一定不是第一选择,在分库分表之前,应该先保证垂直拆分完成了,子系统内都是高内聚的,其次基于Master-Slave的读写分离或者模糊查询很多的,可能NoSQL比如elastic就引流去很大一部分了。当读写分离也做完了,主库只剩下关键业务逻辑之后,流量还是很高,这个时候才开始考虑分库分表。因为相对于读写分离、垂直拆分,分库分表对开发和运维的要求多得多,如果确定业务一两年内不会剧增的,盲目引入只会导致成本高昂(尤其是各种SQL限制)。

其次,分库分表会增加N倍的数据库服务器,一般来说是4的倍数,如果某个应用说要做分库分表,又只有两台机器,那完全就是凑热闹。

读写分离和分库分表应该来说是前后的两件事比较合理,不少人将这两个事情混到一起去讲准确的说不合理的。分库分表通常更多的是用于纯粹的OLTP的事情,让系统可以水平扩展。而读写分离更多的是为了把一部分可以容忍短时延迟/不保证100%(但应该在99%以上)准确的查询路由到查询库,准确的说是对业务根据优先级做个归类,这些查询从资源消耗的角度来说相对逻辑上的PK查询要高几倍到数百倍,比如说查询某个人过去3个月的交易情况,但他从业务角度并不算是DSS概念,比如说查询已经T-N的订单/针对这些订单进行导出,并针对这个单子可能会进行一些操作,甚至人工修改状态。通过把这些功能从核心的订单/交易/资金OLTP中拆分出去,可以保证核心业务系统不会因为一个异常操作比如SQL不合理导致系统出现业务负载增加外的额外抖动因素。

从系统设计角度来说,读写分离虽然从逻辑表结构角度来说是相同的,都具有相同的字段定义,但是物理实现上是一定是不相同(要是完全相同,说明没有领会读写分离的初衷)的,尤其是在索引上,写库可能除了PK、唯一索引(任何表都应该有唯一索引,分布式锁只能作为缓冲器)外,最多还有一两个简单的字段索引以最大化性能,任何SQL符合条件的数据一般不会超过几十条(极端客户除外),但是读库根据业务不同,可能会有很多的索引以满足各种查询以及简单的统计汇总报表的需求。

那写库是不是一定就比读库重要呢?是,又不是。是是绝对的,不是是相对的。因为读库不是DSS库,是交易库中相对来说不是特别重要的业务功能。所以,写库一旦挂了,就会导致业务下不去,读库挂了,可能会导致做业务的人决策错误。比如没有没有查到做过某交易,又重新交易一次。

考虑分库分表一个很重要的决策就是是否允许跨多库操作以及有没有必要。TODO。。。。待补充。。。。。。

其次,分库和分表是两件事,是到底选择分库还是分表,如果量没有那么大的话而且是虚拟机的话,估计分库就够了。

sharding-jdbc的版本及其架构差异

目前最新版本的sharding-jdbc是v3.0.0.M1,应该来说还不稳定,包名又改成了io.shardingsphere,jar包名是sharding-jdbc。

1.5.4.1是目前最新版,也可能是终版,1.x的,坐标是com.dangdang,jar包名是sharding-jdbc。

2.0.3是目前最新版,包名和坐标统一改成了io.shardingjdbc,jar包名是sharding-jdbc-core。

说明规划不是特别好,还是有些乱。

因为2.0之后基本上纯粹分库分表的核心特性的增强就不多了,主要往治理和代理方向去了,所以如果没有特别的需求比如需要类似mycat的独立服务代理模式,使用1.x(注:1.x版本官网文档好像下线了)就可以了,不过如果是大规模的部署,同时已经使用了微服务架构中的注册中心或者基于spring boot,可以考虑使用2.0,因为2.0增加了基于注册中心的配置管理以及spring boot starter。所以2.0的架构是这样的:

技术分享图片

3.0之后,增加了类似mycat角色的sharding-proxy无状态服务器(代理层可以有,但是不应该作为应用直接访问的入口,如下图所示),以及类似于Service Mesh的Database Mesh。不过核心没有变化,对SQL语法增加了部分新的支持。所以3.0的架构如下:

技术分享图片

 

 就分库分表核心来说,我们就只要关心sharding-jdbc就可以了,不需要关心sharding-sphere的其他部分。

sharding-jdbc/Sharding-Proxy/Sharding-Sidecar三者的对比如下:

技术分享图片

 

事务

对任何重要的系统来说,事物一致性都是关键的。对分布式系统来说更是如此,最重要的就是事务一致性,从这个层面来说,分库分表本身并没有引入另外一个层次的复杂性。因为它在jdbc驱动层包了一层,所以我们有必要理解它对事务的支持性以及相关的限制。事务

sharding jdbc 2.x不支持强一致性的分布式事务,一般现在的系统设计也不追求强一致性,而是最终一致性。所以sharding jdbc 2.x支持2中事务:弱XA(同mycat)和最大努力投递事务(官方简称BED)(算是BASE的一种),具体选择哪一种需要根据业务和开发进行权衡,如果架构规范和设计做得好,是可以做到不跨库分布式事务的

弱XA事务是默认的模式(即只要dml期间没有抛出异常,commit期间有机器断网或者宕机,是无法保证一致性的),没有特别的要求。

BED则在一定上增加了短时容忍,将执行的语句另作中心化存储,然后轮询commit期间失败的事务重试,所以BED的架构如下:

技术分享图片

 

但是没有免费的午餐,BED对开发和维护有着一定的额外要求,而且这些要求都涉及面很广,绝对算得上伤筋动骨。开发层面包括:


  1. INSERT语句的主键不能自增

  2. UPDATE必须可重复执行,比如不支持UPDATE xxx SET x=x+1,对于更新余额类,这就相当于要求必须乐观锁了

运维层面包括:


  1. 需要存储事务日志的数据库

  2. 用于异步作业使用的zookeeper

  3. 解压sharding-jdbc-transaction-async-job-$VERSION.tar,通过start.sh脚本启动异步作业

我们选择了从设计层面避免强一致性的分布式事务。

分片灵活性

对于分库分表来说,很重要的一个特性是分片的灵活性,比如单个字段、多个字段的=、IN、>=、<=。为什么多个字段很重要的,这里涉及到一个特殊的考虑

sharding-jdbc目前提供4种分片算法。

由于分片算法和业务实现紧密相关,因此并未提供内置分片算法,而是通过分片策略将各种场景提炼出来,提供更高层级的抽象,并提供接口让应用开发者自行实现分片算法。


  • 精确分片算法

对应PreciseShardingAlgorithm,用于处理使用单一键作为分片键的=与IN进行分片的场景。需要配合StandardShardingStrategy使用。


  • 范围分片算法

对应RangeShardingAlgorithm,用于处理使用单一键作为分片键的BETWEEN AND进行分片的场景。需要配合StandardShardingStrategy使用。


  • 复合分片算法

对应ComplexKeysShardingAlgorithm,用于处理使用多键作为分片键进行分片的场景,多分片键逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合ComplexShardingStrategy使用。


  • Hint分片算法(Hint分片指的是对于分片字段非SQL决定,而由其他外置条件决定的场景,可使用SQL Hint灵活的注入分片字段。例:内部系统,按照员工登录ID分库,而数据库中并无此字段。SQL Hint支持通过Java API和SQL注释(待实现)两种方式使用。)

对应HintShardingAlgorithm,用于处理使用Hint行分片的场景。需要配合HintShardingStrategy使用。

因为算法的灵活性,标准的方式是通过实现具体的java接口是实现具体的分片算法比如SingleKeyDatabaseShardingAlgorithm,有不少的情况下,分片是比较简单的,比如说纯粹是客户编号,此时提供了行内表达式分片策略,使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,不过这只支持单分片键。比如,t_user_${u_id % 8} 表示t_user表按照u_id按8取模分成8个表,表名称为t_user_0t_user_7

分片键+分片算法=真正可用的分片策略。

算法和分片键的选择是分库分表的关键,其直接决定了各个分库的负载是否均衡,以及扩展是否容易。在设计上的考虑一节笔者会详细阐述,订单和委托业务、用户在使用分库分表时设计上的考虑以及原因。 

语法限制

对于分库分表来说,还需要知道有哪些SQL的限制,尤其是涉及到需要二次处理的,比如排序,去重,聚合等。

这里笔者就列下那些常用但没有被支持的。比如:


  • case when

  • distinct

  • union

不过好在这些在java/js中处理都比较方便。

如果有很复杂的SQL,那最大的可能就是设计上有问题,应该采用读写分离解决。

sharding-jdbc对SQL的限制完整可以参考http://shardingsphere.io/document/current/cn/features/sharding/usage-standard/sql/

设计上的考虑

哪些表要分库分表

首先从设计上要区分清楚哪些是广播表/哪些是分库表/哪些是只在一个库的表,因为是公用数据源的,所以不管是不是分库的表,都需要配置,不配置分片规则Sharding-JDB即无法精确的断定应该路由至哪个数据源。但是一般分库分表组件包括Sharding-JDBC都会提供简化配置的方法。对于不分片的表:

方法1:sharding-jdbc可以在配置default-data-source-name,这样未配置分片规则的表将通过默认数据源定位。

方法2:将不参与分库分表的数据源独立于Sharding-JDBC之外,在应用中使用多个数据源分别处理分片和不分片的情况。

分库还是分表

 

分片键的选择

 其中最重要的是分片键不能是自增字段,否则insert就不知道去哪里了。

分布式主键

 

Sharding-JDBC使用说明

对于只有一个分片键的使用=和IN进行分片的SQL,建议使用行表达式代替Java类的配置。假设我们不使用弱性事务(如果使用柔性事务,则还需要引入sharding-jdbc-transaction以及sharding-jdbc-transaction-async-job),这样就只要引入sharding-jdbc-core这个jar包就可以了,(因为sharding-jdbc的配置支持java、yaml、spring boot以及spring命名空间(类似dubbo),所以建议使用spring 命名空间方式)如下:

<dependency>
<groupId>io.shardingjdbcgroupId>
<artifactId>sharding-jdbc-coreartifactId>
<version>2.0.3version>
dependency>

<dependency>
<groupId>io.shardingjdbcgroupId>
<artifactId>sharding-jdbc-core-spring-namespaceartifactId>
<version>2.0.3version>
dependency>

因为sharding jdbc可以支持dbcp、druid,所以就不用改动其他的依赖项了。

接下去配置数据源、数据源分片策略和表分片策略。

注意点:

1、使用了sharding-jdbc之后,select from TableName会被转换为select from tablename,也就是转小写,这是在代码中处理的,如果不希望转换(sql标准是大小写不敏感的,主要是mysql linux下lower-case-table-names=1这个特立会区分大小写,而且mysql 8.0不允许数据库初始化和启动的值不一致,5.7之前是可以的,https://bugs.mysql.com/bug.php?id=90695);,则需要自己编译sharding-jdbc,并更改getTableTokens中的转小写的代码(这应该算是个bug),2.0.3版本代码位置为:

// io.shardingjdbc.core.rewrite.SQLRewriteEngine.getTableTokens(TableUnit)中对sql语句中的表名做了toLowerCase()导致的,如下:
private Map getTableTokens(final TableUnit tableUnit) {
String logicTableName
= tableUnit.getLogicTableName().toLowerCase();
Map
tableTokens = new HashMap<>();
tableTokens.put(logicTableName, tableUnit.getActualTableName());
Optional
bindingTableRule = shardingRule.findBindingTableRule(logicTableName);
if (bindingTableRule.isPresent()) {
tableTokens.putAll(getBindingTableTokens(tableUnit, bindingTableRule.get()));
}
return tableTokens;
}

2、一定要设置context:property-placeholder的ignore-unresolvable属性为true,即<context:property-placeholder location="classpath:property/*.properties" ignore-unresolvable="true" />,否则会报无法解析占位符。

参考:

https://www.oschina.net/question/2918182_2280300

https://www.oschina.net/news/88860/sharding-jdbc-1-5-4-released

http://www.infoq.com/cn/news/2017/12/Sharding-JDBC-2

http://cmsblogs.com/?p=2542

http://shardingjdbc.io/document/legacy/2.x/en/00-overview/

https://www.oschina.net/question/2356021_2264290 

https://blog.csdn.net/vkingnew/article/details/80613043


推荐阅读
  • 分布式大型互联网企业架构!
    2019独角兽企业重金招聘Python工程师标准摘要:开发工具1.EclipseIDE:采用Maven项目管理,模块化。2.代码生成: ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
  • 分享css中提升优先级属性!important的用法总结
    web前端|css教程css!importantweb前端-css教程本文分享css中提升优先级属性!important的用法总结微信门店展示源码,vscode如何管理站点,ubu ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • Linux如何安装Mongodb的详细步骤和注意事项
    本文介绍了Linux如何安装Mongodb的详细步骤和注意事项,同时介绍了Mongodb的特点和优势。Mongodb是一个开源的数据库,适用于各种规模的企业和各类应用程序。它具有灵活的数据模式和高性能的数据读写操作,能够提高企业的敏捷性和可扩展性。文章还提供了Mongodb的下载安装包地址。 ... [详细]
  • ubuntu用sqoop将数据从hive导入mysql时,命令: ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • 我正在使用sql-serverkafka-connect和debezium监视sqlserver数据库,但是当我发布并运行我的wo ... [详细]
  • Zookeeper 总结与面试题汇总
    Zookeeper总结与面试题汇总,Go语言社区,Golang程序员人脉社 ... [详细]
author-avatar
LiangChao
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有