热门标签 | 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


推荐阅读
  • 前言无论是对于刚入行工作还是已经工作几年的java开发者来说,面试求职始终是你需要直面的一件事情。首先梳理自己的知识体系,针对性准备,会有事半功倍的效果。我们往往会把重点放在技术上 ... [详细]
  • 本文探讨了如何在日常工作中通过优化效率和深入研究核心技术,将技术和知识转化为实际收益。文章结合个人经验,分享了提高工作效率、掌握高价值技能以及选择合适工作环境的方法,帮助读者更好地实现技术变现。 ... [详细]
  • 科研单位信息系统中的DevOps实践与优化
    本文探讨了某科研单位通过引入云原生平台实现DevOps开发和运维一体化,显著提升了项目交付效率和产品质量。详细介绍了如何在实际项目中应用DevOps理念,解决了传统开发模式下的诸多痛点。 ... [详细]
  • 全面解析运维监控:白盒与黑盒监控及四大黄金指标
    本文深入探讨了白盒和黑盒监控的概念,以及它们在系统监控中的应用。通过详细分析基础监控和业务监控的不同采集方法,结合四个黄金指标的解读,帮助读者更好地理解和实施有效的监控策略。 ... [详细]
  • 本文深入探讨了MySQL中常见的面试问题,包括事务隔离级别、存储引擎选择、索引结构及优化等关键知识点。通过详细解析,帮助读者在面对BAT等大厂面试时更加从容。 ... [详细]
  • 探讨如何真正掌握Java EE,包括所需技能、工具和实践经验。资深软件教学总监李刚分享了对毕业生简历中常见问题的看法,并提供了详尽的标准。 ... [详细]
  • 深入解析Spring Cloud微服务架构与分布式系统实战
    本文详细介绍了Spring Cloud在微服务架构和分布式系统中的应用,结合实际案例和最新技术,帮助读者全面掌握微服务的实现与优化。 ... [详细]
  • Spring Cloud学习指南:深入理解微服务架构
    本文介绍了微服务架构的基本概念及其在Spring Cloud中的实现。讨论了微服务架构的主要优势,如简化开发和维护、快速启动、灵活的技术栈选择以及按需扩展的能力。同时,也探讨了微服务架构面临的挑战,包括较高的运维要求、分布式系统的复杂性、接口调整的成本等问题。最后,文章提出了实施微服务时应遵循的设计原则。 ... [详细]
  • 本文探讨了大型服务端开发过程中常见的几个误区,包括异步任务处理不当、日志同步模式使用、网络操作未设置超时、缓存命中率及响应时间未统计、单一缓存模式、分布式缓存加锁不当以及团队管理上的误区,旨在帮助开发者避免这些常见错误。 ... [详细]
  • 深入解析Serverless架构模式
    本文将详细介绍Serverless架构模式的核心概念、工作原理及其优势。通过对比传统架构,探讨Serverless如何简化应用开发与运维流程,并介绍当前主流的Serverless平台。 ... [详细]
  • 本文详细介绍了如何在 Android 中使用值动画(ValueAnimator)来动态调整 ImageView 的高度,并探讨了相关的关键属性和方法,包括图片填充后的高度、原始图片高度、动画变化因子以及布局重置等。 ... [详细]
  • 本文探讨了现代分布式架构的多样性,包括高并发、多活数据中心、容器化、微服务、高可用性和弹性架构等,并介绍了与这些架构相关的重要管理技术,如DevOps、应用监控和自动化运维。文章还深入分析了分布式系统的核心概念、主要用途及类型,同时对比了单体应用与分布式服务化的优缺点。 ... [详细]
  • 2023年最佳PHP开发学习路径推荐
    本文详细探讨了针对不同背景的学习者如何选择最适合自己的PHP开发学习资源,包括书籍、在线课程及培训机构的推荐。 ... [详细]
  • APM(Application Performance Management,应用性能管理)对于提供互联网服务的企业至关重要。本文将深入探讨APM如何帮助识别和解决导致用户流失的技术问题,以及它在提升整体用户体验方面的作用。 ... [详细]
  • 忙而不盲:探索高效工作的艺术
    本文探讨了在日常工作中如何避免盲目忙碌,通过理解和应对稀有事件、管理负面情绪以及明确学习目标,提升工作效率和个人成长。 ... [详细]
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社区 版权所有