| <?xml version="1.0" encoding="utf-8" standalone="yes" ?> |
| <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> |
| <channel> |
| <title>02-guides on </title> |
| <link>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/</link> |
| <description>Recent content in 02-guides on </description> |
| <generator>Hugo -- gohugo.io</generator> |
| |
| <atom:link href="http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/index.xml" rel="self" type="application/rss+xml" /> |
| |
| |
| <item> |
| <title>使用指南</title> |
| <link>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/</link> |
| <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> |
| |
| <guid>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/</guid> |
| <description> 本章导航 想一览基本的概念,请阅读基本概念。 想通过一个完整的例子使用Sharding-JDBC进行分库分表,请阅读如何分库分表章节。 分库分表后,要进一步提高性能,可以使用读写分离功能。 配置手册介绍了如何通过YAML和Spring两种方式配置。 一般情况Sharding-JDBC是通过SQL解析来获取数据路由数据的,但也可以使用强制路由方式来设置分片数据。该模式非常适合存量系统扩容。 分库分表后主键如何生成?分布式主键将给你答案。 分库后事务如何保证?事务支持将介绍Sharding-JDBC如何通过使用柔性事务的方式来实现分布式事务。 分页是否影响性能?Oracle和SQLServer的分页涉及到子查询又该如何处理?请阅读分页及子查询。 Sharding-JDBC如何运行单元测试和整合测试?请阅读测试引擎。 </description> |
| </item> |
| |
| <item> |
| <title>核心概念</title> |
| <link>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/concepts/</link> |
| <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> |
| |
| <guid>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/concepts/</guid> |
| <description>本文介绍Sharding-JDBC包含的一些核心概念。 |
| LogicTable 数据分片的逻辑表,对于水平拆分的数据库(表),同一类表的总称。例:订单数据根据主键尾数拆分为10张表,分别是t_order_0到t_order_9,他们的逻辑表名为t_order。 |
| ActualTable 在分片的数据库中真实存在的物理表。即上个示例中的t_order_0到t_order_9。 |
| DataNode 数据分片的最小单元。由数据源名称和数据表组成,例:ds_1.t_order_0。配置时默认各个分片数据库的表结构均相同,直接配置逻辑表和真实表对应关系即可。如果各数据库的表结果不同,可使用ds.actual_table配置。 |
| DynamicTable 逻辑表和真实表不一定需要在配置规则中静态配置。比如按照日期分片的场景,真实表的名称随着时间的推移会产生变化。此类需求Sharding-JDBC是支持的,不过目前配置并不友好,会在新版本中提升。 |
| BindingTable 指在任何场景下分片规则均一致的主表和子表。例:订单表和订单项表,均按照订单ID分片,则此两张表互为BindingTable关系。BindingTable关系的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。 |
| ShardingColumn 分片字段。用于将数据库(表)水平拆分的关键字段。例:订单表订单ID分片尾数取模分片,则订单ID为分片字段。SQL中如果无分片字段,将执行全路由,性能较差。Sharding-JDBC支持多分片字段。 |
| ShardingAlgorithm 分片算法。Sharding-JDBC通过分片算法将数据分片,支持通过等号、BETWEEN和IN分片。分片算法目前需要业务方开发者自行实现,可实现的灵活度非常高。未来Sharding-JDBC也将会实现常用分片算法,如range,hash和tag等。 |
| SQL Hint 对于分片字段非SQL决定,而由其他外置条件决定的场景,可使用SQL Hint灵活的注入分片字段。例:内部系统,按照员工登录ID分库,而数据库中并无此字段。SQL Hint支持通过ThreadLocal和SQL注释(待实现)两种方式使用。</description> |
| </item> |
| |
| <item> |
| <title>分库分表</title> |
| <link>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/sharding/</link> |
| <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> |
| |
| <guid>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/sharding/</guid> |
| <description>阅读本指南前,请先阅读快速起步。本文档使用更复杂的场景进一步介绍Sharding-JDBC的分库分表能力。 |
| 数据库模式 本文档中提供了两个数据源db0和db1,每个数据源之中包含了两组表t_order_0和t_order_1,t_order_item_0和t_order_item_1 。这两组表的建表语句为: |
| CREATE TABLE IF NOT EXISTS t_order_x ( order_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY (order_id) ); CREATE TABLE IF NOT EXISTS t_order_item_x ( item_id INT NOT NULL, order_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY (item_id) ); 逻辑表与实际表映射关系 均匀分布 数据表在每个数据源内呈现均匀分布的态势 |
| db0 ├── t_order_0 └── t_order_1 db1 ├── t_order_0 └── t_order_1 表规则可以使用默认的配置 |
| TableRule orderTableRule = TableRule.builder(&quot;t_order&quot;).actualTables(Arrays.asList(&quot;t_order_0&quot;, &quot;t_order_1&quot;)).dataSourceRule(dataSourceRule).build(); 自定义分布 数据表呈现有特定规则的分布</description> |
| </item> |
| |
| <item> |
| <title>读写分离</title> |
| <link>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/master-slave/</link> |
| <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> |
| |
| <guid>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/master-slave/</guid> |
| <description>概念 为了缓解数据库压力,将写入和读取操作分离为不同数据源,写库称为主库,读库称为从库,一主库可配置多从库。 |
| 支持项 提供了一主多从的读写分离配置,可独立使用,也可配合分库分表使用。 同一线程且同一数据库连接内,如有写入操作,以后的读操作均从主库读取,用于保证数据一致性。 Spring命名空间。 基于Hint的强制主库路由。 不支持范围 主库和从库的数据同步。 主库和从库的数据同步延迟导致的数据不一致。 主库双写或多写。 代码开发示例 // 构建读写分离数据源, 读写分离数据源实现了DataSource接口, 可直接当做数据源处理. masterDataSource0, slaveDataSource00, slaveDataSource01等为使用DBCP等连接池配置的真实数据源 Map&lt;String, DataSource&gt; slaveDataSourceMap0 = new HashMap&lt;&gt;(); slaveDataSourceMap0.put(&quot;slaveDataSource00&quot;, slaveDataSource00); slaveDataSourceMap0.put(&quot;slaveDataSource01&quot;, slaveDataSource01); // 可选择主从库负载均衡策略, 默认是ROUND_ROBIN, 还有RANDOM可以选择, 或者自定义负载策略 DataSource masterSlaveDs0 = MasterSlaveDataSourceFactory.createDataSource(&quot;ms_0&quot;, &quot;masterDataSource0&quot;, masterDataSource0, slaveDataSourceMap0, MasterSlaveLoadBalanceStrategyType.ROUND_ROBIN); Map&lt;String, DataSource&gt; slaveDataSourceMap1 = new HashMap&lt;&gt;(); slaveDataSourceMap1.put(&quot;slaveDataSource10&quot;, slaveDataSource10); slaveDataSourceMap1.put(&quot;slaveDataSource11&quot;, slaveDataSource11); DataSource masterSlaveDs1 = MasterSlaveDataSourceFactory.createDataSource(&quot;ms_1&quot;, &quot;masterDataSource1&quot;, masterDataSource1, slaveDataSourceMap1, MasterSlaveLoadBalanceStrategyType.ROUND_ROBIN); // 构建分库分表数据源 Map&lt;String, DataSource&gt; dataSourceMap = new HashMap&lt;&gt;(); dataSourceMap.</description> |
| </item> |
| |
| <item> |
| <title>配置手册</title> |
| <link>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/configuration/</link> |
| <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> |
| |
| <guid>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/configuration/</guid> |
| <description>YAML配置 引入maven依赖 &lt;dependency&gt; &lt;groupId&gt;com.dangdang&lt;/groupId&gt; &lt;artifactId&gt;sharding-jdbc-config-yaml&lt;/artifactId&gt; &lt;version&gt;${latest.release.version}&lt;/version&gt; &lt;/dependency&gt; Java示例 DataSource dataSource = new YamlShardingDataSource(yamlFile); 配置示例 dataSource: ds_0: !!org.apache.commons.dbcp.BasicDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/ds_0 username: root password: ds_1: !!org.apache.commons.dbcp.BasicDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/ds_1 username: root password: defaultDataSourceName: ds_0 tables: config: actualTables: t_config t_order: actualTables: t_order_${0..1} tableStrategy: &amp;table001 shardingColumns: order_id algorithmExpression: t_order_${order_id.longValue() % 2} #绑定表中其余的表的策略与t_order的策略相同 t_order_item: actualTables: t_order_item_${0..1} tableStrategy: *table001 bindingTables: - tableNames: t_order,t_order_item - tableNames: ... defaultDatabaseStrategy: shardingColumns: none algorithmClassName: com.</description> |
| </item> |
| |
| <item> |
| <title>强制路由</title> |
| <link>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/hint-sharding-value/</link> |
| <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> |
| |
| <guid>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/hint-sharding-value/</guid> |
| <description>提示:阅读本文前请详细预读 分库分表 |
| 背景 对Sharding-JDBC有初步了解的朋友已经发现了:在编写分片算法的时候,传入的分片键值是来自SQL语句中WHERE条件的。 例如逻辑表t_order如果其数据源分片键为user_id, 分片算法是奇数值路由到db1偶数值路由到db2;表分片键为order_id, 分片算法是奇数值路由到t_order_1偶数值路由到t_order_2,如果执行如下sql语句: |
| select * from t_order where user_id = 1 and order_id = 2 那么在数据源分片算法的shardingValue参数将会传入1用于分片计算,结果为路由到db1; 表分片算法的shardingValue参数将会传入2用于分片计算,结果为路由到t_order_2。最终SQL为: |
| select * from db1.t_order_2 where user_id = 1 and order_id = 2 现有一个假设,如果WHERE中没有user_id和order_id的条件,那么是否可以进行分片计算呢? |
| 答案是肯定的。下面就介绍一下Sharding-JDBC对这个问题的解决方法。 |
| 基于暗示(Hint)的分片键值管理器 要解决上面的问题,我们使用com.dangdang.ddframe.rdb.sharding.api.HintManager。 该管理器是使用ThreadLocal技术管理分片键值的。 使用例子: |
| String sql = &quot;SELECT * FROM t_order&quot;; try ( HintManager hintManager = HintManager.getInstance(); Connection conn = dataSource.getConnection(); PreparedStatement preparedStatement = conn.prepareStatement(sql)) { hintManager.addDatabaseShardingValue(&quot;t_order&quot;, &quot;user_id&quot;, 1); hintManager.addTableShardingValue(&quot;t_order&quot;, &quot;order_id&quot;, 2); try (ResultSet rs = preparedStatement.</description> |
| </item> |
| |
| <item> |
| <title>分布式主键</title> |
| <link>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/key-generator/</link> |
| <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> |
| |
| <guid>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/key-generator/</guid> |
| <description>实现动机 传统数据库软件开发中,主键自动生成技术是基本需求。而各大数据库对于该需求也提供了相应的支持,比如MySQL的自增键。 对于MySQL而言,分库分表之后,不同表生成全局唯一的Id是非常棘手的问题。因为同一个逻辑表内的不同实际表之间的自增键是无法互相感知的, 这样会造成重复Id的生成。我们当然可以通过约束表生成键的规则来达到数据的不重复,但是这需要引入额外的运维力量来解决重复性问题,并使框架缺乏扩展性。 |
| 目前有许多第三方解决方案可以完美解决这个问题,比如UUID等依靠特定算法自生成不重复键,或者通过引入Id生成服务等。 但也正因为这种多样性导致了Sharding-JDBC如果强依赖于任何一种方案就会限制其自身的发展。 |
| 基于以上的原因,最终采用了以JDBC接口来实现对于生成Id的访问,而将底层具体的Id生成实现分离出来。 |
| 使用方法 使用方法分为设置自动生成键和获取生成键两部分 |
| 设置自动生成键 配置自增列: |
| TableRule.builder(&quot;t_order&quot;).generateKeyColumns(&quot;order_id&quot;); 设置Id生成器的实现类,该类必须实现com.dangdang.ddframe.rdb.sharding.keygen.KeyGenerator接口。 |
| 配置全局生成器(com.xx.xx.KeyGenerator): |
| ShardingRule.builder().keyGenerator(com.xx.xx.KeyGenerator.class); 有时候我们希望部分表的Id生成器与全局Id生成器不同,比如t_order_item表希望使用com.xx.xx.OtherKeyGenerator来生成Id: |
| TableRule.builder(&quot;t_order_item&quot;).generateKeyColumns(&quot;order_item_id&quot;, com.xx.xx.OtherKeyGenerator.class); 这样t_order就使用com.xx.xx.KeyGenerator生成Id,而t_order_item使用com.xx.xx.OtherKeyGenerator生成Id。 |
| 获取自动生成键 通过JDBC提供的API来获取。对于Statement来说调用statement.execute(&quot;INSERT ...&quot;, Statement.RETURN_GENERATED_KEYS) 来通知需要返回的生成的键值。对于PreparedStatement则是connection.prepareStatement(&quot;INSERT ...&quot;, Statement.RETURN_GENERATED_KEYS) |
| 调用statement.getGeneratedKeys()来获取键值的ResultSet。 |
| 其他框架配置 关于Spring,YAML,MyBatis和JPA(Hibernate)的配置请参考 示例工程。 |
| 默认的分布式主键生成器 类名称:com.dangdang.ddframe.rdb.sharding.keygen.DefaultKeyGenerator |
| 该生成器采用snowflake算法实现,生成的数据为64bit的long型数据。 在数据库中应该用大于等于64bit的数字类型的字段来保存该值,比如在MySQL中应该使用BIGINT。 |
| 其二进制表示形式包含四部分,从高位到低位分表为:1bit符号位(为0),41bit时间位,10bit工作进程位,12bit序列位。 |
| 时间位(41bit) 从2016年11月1日零点到现在的毫秒数,时间可以使用到2156年,满足大部分系统的要求。 |
| 工作进程位(10bit) 该标志在Java进程内是唯一的,如果是分布式应用部署应保证每个进程的工作进程Id是不同的。该值默认为0,可通过调用静态方法DefaultKeyGenerator.setWorkerId(&quot;xxxx&quot;)设置。 |
| 序列位(12bit) 该序列是用来在同一个毫秒内生成不同的Id。如果在这个毫秒内生成的数量超过4096(2的12次方),那么生成器会等待到下个毫秒继续生成。 |
| 总结 从Id的组成部分看,不同进程的Id肯定是不同的,同一个进程首先是通过时间位保证不重复,如果时间相同则是通过序列位保证。 同时由于时间位是单调递增的,且各个服务器如果大体做了时间同步,那么生成的Id在分布式环境可以认为是总体有序的。 这就保证了对索引字段的插入的高效性。例如MySQL的Innodb存储引擎的主键。</description> |
| </item> |
| |
| <item> |
| <title>事务支持</title> |
| <link>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/transaction/</link> |
| <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> |
| |
| <guid>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/transaction/</guid> |
| <description>Sharding-JDBC由于性能方面的考量,决定不支持强一致性分布式事务。我们已明确规划线路图,未来会支持最终一致性的柔性事务。 |
| 目前最大努力送达型柔性事务已开发完成。 |
| 如果不使用柔性事务,也会自动包含弱XA事务支持,有以下几点说明: |
| 完全支持非跨库事务,例如:仅分表,或分库但是路由的结果在单库中。 |
| 完全支持因逻辑异常导致的跨库事务。例如:同一事务中,跨两个库更新。更新完毕后,抛出空指针,则两个库的内容都能回滚。 |
| 不支持因网络、硬件异常导致的跨库事务。例如:同一事务中,跨两个库更新,更新完毕后、未提交之前,第一个库死机,则只有第二个库数据提交。 |
| 柔性事务 最大努力送达型 概念 在分布式数据库的场景下,相信对于该数据库的操作最终一定可以成功,所以通过最大努力反复尝试送达操作。 |
| 架构图 适用场景 根据主键删除数据。 更新记录永久状态,如更新通知送达状态。 使用限制 使用最大努力送达型柔性事务的SQL需要满足幂等性。 |
| INSERT语句要求必须包含主键,且不能是自增主键。 UPDATE语句要求幂等,不能是UPDATE xxx SET x=x+1 DELETE语句无要求。 开发指南 sharding-jdbc-transaction完全基于java开发,直接提供jar包,可直接使用maven导入坐标即可使用。 为了保证事务不丢失,sharding-jdbc-transaction需要提供数据库存储事务日志,配置方法可参见事务管理器配置项。 由于柔性事务采用异步尝试,需要部署独立的作业和Zookeeper。sharding-jdbc-transaction采用elastic-job实现的sharding-jdbc-transaction-async-job,通过简单配置即可启动高可用作业异步送达柔性事务,启动脚本为start.sh。 为了便于开发,sharding-jdbc-transaction提供了基于内存的事务日志存储器和内嵌异步作业。 开发示例 // 1. 配置SoftTransactionConfiguration SoftTransactionConfiguration transactionConfig = new SoftTransactionConfiguration(dataSource); transactionConfig.setXXX(); // 2. 初始化SoftTransactionManager SoftTransactionManager transactionManager = new SoftTransactionManager(transactionConfig); transactionManager.init(); // 3. 获取BEDSoftTransaction BEDSoftTransaction transaction = (BEDSoftTransaction) transactionManager.getTransaction(SoftTransactionType.BestEffortsDelivery); // 4. 开启事务 transaction.begin(connection); // 5.</description> |
| </item> |
| |
| <item> |
| <title>分页及子查询</title> |
| <link>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/subquery/</link> |
| <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> |
| |
| <guid>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/subquery/</guid> |
| <description>Sharding-JDBC完全支持MySQL、PostgreSQL和Oracle的分页查询,SQLServer由于分页查询较为复杂,仅部分支持。 |
| 分页性能 性能瓶颈 查询偏移量过大的分页会导致数据库获取数据性能低下,以MySQL为例: |
| SELECT * FROM t_order ORDER BY id LIMIT 1000000, 10 这句SQL会使得MySQL在无法利用索引的情况下跳过1000000条记录后,再获取10条记录,其性能可想而知。而在分库分表的情况下(假设分为2个库),为了保证数据的正确性,SQL会改写为: |
| SELECT * FROM t_order ORDER BY id LIMIT 0, 1000010 即将偏移量前的记录全部取出,并仅获取排序后的最后10条记录。这会在数据库本身就执行很慢的情况下,进一步加剧性能瓶颈。因为原SQL仅需要传输10条记录至客户端,而改写之后的SQL则会传输1000010*2的记录至客户端。 |
| Sharding-JDBC的优化 Sharding-JDBC进行了2个方面的优化。 |
| 首先,Sharding-JDBC采用流式处理 + 归并排序的方式来避免内存的过量占用。Sharding-JDBC的SQL改写,不可避免的占用了额外的带宽,但并不会导致内存暴涨。 与直觉不同,大多数人认为Sharding-JDBC会将1000010*2记录全部加载至内存,进而占用大量内存而导致内存溢出。 但由于每个结果集的记录是有序的,因此Sharding-JDBC每次比较仅获取各个分片的当前结果集记录,驻留在内存中的记录仅为当前路由到的分片的结果集的当前游标指向而已。 对于本身即有序的待排序对象,归并排序的时间复杂度仅为O(n),性能损耗很小。 |
| 其次,Sharding-JDBC对仅落至单分片的查询进行进一步优化。落至单分片查询的请求并不需要改写SQL也可以保证记录的正确性,因此在此种情况下,Sharding-JDBC并未进行SQL改写,从而达到节省带宽的目的。 |
| 更好的分页解决方案 由于LIMIT并不能通过索引查询数据,因此如果可以保证ID的连续性,通过ID进行分页是比较好的解决方案: |
| SELECT * FROM t_order WHERE id &gt; 100000 AND id &lt;= 100010 ORDER BY id 或通过记录上次查询结果的最后一条记录的ID进行下一页的查询: |
| SELECT * FROM t_order WHERE id &gt; 100000 LIMIT 10 分页子查询 Oracle和SQLServer的分页都需要通过子查询来处理,Sharding-JDBC支持分页相关的子查询。</description> |
| </item> |
| |
| <item> |
| <title>测试引擎</title> |
| <link>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/test-framework/</link> |
| <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> |
| |
| <guid>http://shardingsphere.apache.org/document/legacy/1.x/cn/02-guide/test-framework/</guid> |
| <description>Sharding-JDBC提供了完善的测试引擎。它以XML方式定义SQL,每条SQL由SQL解析单元测试引擎和整合测试引擎驱动,每个引擎分别为H2、MySQL、PostgreSQL、SQLServer和Oracle数据库运行测试用例。 |
| SQL解析单元测试全面覆盖SQL占位符和字面量维度。整合测试进一步拆分为策略和JDBC两个维度,策略维度包括分库分表、仅分表、仅分库、读写分离、动态表等策略,JDBC维度包括Statement、PreparedStatement。 |
| 因此,1条SQL会驱动5种数据库的解析 * 2种参数传递类型 + 5种数据库 * 5种分片策略 * 2种JDBC运行方式 = 60个测试用例,以达到Sharding-JDBC对于高质量的追求。 |
| 整合测试 测试环境 整合测试由于涉及到真实数据库环境,需要先完成以下准备工作并测试: |
| 在准备测试的数据库上运行resources/integrate/schema/manual_schema_create.sql创建数据库(MySQL、PostgreSQL、SQLServer)及Schema(仅Oracle)。 |
| 修改sharding-jdbc-core/src/test/resources/integrate/env.properties中的databases,指定需要测试的数据库。 |
| 运行AllIntegrateTests,检查测试结果。 |
| 注意事项 如需测试Oracle,请在POM文件中增加Oracle驱动依赖。 |
| 为了保证测试数据的完整性,整合测试中的分库分表采用了10库10表的方式,因此运行测试用例的时间会比较长。 |
| SQL解析引擎测试 测试环境 SQL解析引擎测试是基于SQL本身的解析,因此无需连接数据库,直接运行AllParsingTests即可。</description> |
| </item> |
| |
| </channel> |
| </rss> |