Apache ShardingSphere 是一套開源的分散式資料庫解決方案組成的生態圈,它由 JDBC、Proxy 和 Sidecar 這 3 款既能夠獨立部署,又支援混合部署配合使用的產品組成。它們均提供標準化的資料水平擴充套件、分散式事務和分散式治理等功能,可適用於如 Java 同構、異構語言、雲原生等各種多樣化的應用場景。
ShardingSphere 是一個很活躍的專案,當前穩定版是 4.x ,預覽版 5.x 及文件早已釋出。ShardingSphere 早期 3.x 之前版本和 4.x 之後版本配置不相容,而且從 4.x 開始才修復解析 select for update 語句問題,並且嚴格校驗 schema ,不允許跨庫查詢。
本章基於 4.x 版本,使用 Sharding JDBC 實現分庫分表,並配置 Sharding Proxy 和 Sharding UI 實現聚合查詢。
Sharding JDBCSharding JDBC 定位為輕量級Java框架,在Java的JDBC層提供的額外服務。它使用客戶端直連資料庫,以jar包形式提供服務,無需額外部署和依賴,可理解為增強版的JDBC驅動,完全相容JDBC和各種ORM框架。
引入依賴Sharding JDBC:
<dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>${version}</version></dependency>
Spring JDBC 和 MySQL 驅動:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId></dependency>
配置配置資料來源
spring: shardingsphere: props: show-sql: true # 列印sql語句,除錯時可開啟 datasource: names: master1,slave1,slave2 # 配置三個資料來源,主庫master1,從庫slave1和slave2 master1: type: org.apache.commons.dbcp2.BasicDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://${MYSQL_HOST:localhost}:3307/engrz?useSSL=false&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai username: ${MYSQL_USER:engrz} password: ${MYSQL_PASSWORD:engrz2021} slave1: type: org.apache.commons.dbcp2.BasicDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://${MYSQL_HOST:localhost}:3308/engrz?useSSL=false&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai username: ${MYSQL_USER:engrz} password: ${MYSQL_PASSWORD:engrz2021} slave2: type: org.apache.commons.dbcp2.BasicDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://${MYSQL_HOST:localhost}:3309/engrz?useSSL=false&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai username: ${MYSQL_USER:engrz} password: ${MYSQL_PASSWORD:engrz2021}
讀寫分離和分庫分表
spring: shardingsphere: sharding: master-slave-rules: ds1: # 讀寫分離資料來源,如果有多個庫可配置多個 master-data-source-name: master1 slave-data-source-names: slave1,slave2 tables: t_user_info: # 表名 actual-data-nodes: ds1.t_user_info_${0..9} # 規則,使用Groovy語法 table-strategy: inline: sharding-column: user_id # 分片欄位 algorithm-expression: t_user_info_${user_id % 10} t_user_log: # 表名 actual-data-nodes: ds1.t_user_log_$->{2019..2021} # 規則,使用Groovy語法 table-strategy: standard: sharding-column: log_date # 分片欄位 precise-algorithm-class-name: com.engrz.commons.sharding.jdbc.algorithm.DatePreciseModuloShardingTableAlgorithm range-algorithm-class-name: com.engrz.commons.sharding.jdbc.algorithm.DateRangeModuloShardingTableAlgorithm
Sharding JDBC 配置單純的讀寫分離和分庫分表的讀寫分離配置寫法有一點差別僅使用讀寫分離配置屬性:spring.shardingsphere.masterslave.*分庫分表的讀寫分離配置屬性:spring.shardingsphere.sharding.master-slave-rules.*
配置檔案中分片說明:
t_user (使用者表)
分10張表,user_id欄位為long型主鍵,使用 user_id 值取模,把計算結果拼上表名,完整名如:t_user_info_0、t_user_info_1
t_user_log (使用者日誌表)
按年份分表,從2019到2021,使用自定義分片策略
DatePreciseModuloShardingTableAlgorithm:
/** * 日期精確匹配 */public class DatePreciseModuloShardingTableAlgorithm implements PreciseShardingAlgorithm<Date> { @Override public String doSharding(Collection<String> collection, PreciseShardingValue<Date> preciseShardingValue) { Date date = preciseShardingValue.getValue(); Calendar c = Calendar.getInstance(); c.setTime(date); String year = String.valueOf(c.get(Calendar.YEAR)); String tableName = null; for (String tmp : collection) { if (tmp.endsWith(year)) { // 如果以當前年份結尾 tableName = tmp; break; } } if (null == tableName) { String str = collection.iterator().next(); tableName = str.substring(0, str.lastIndexOf("_")); } return tableName; }}
DateRangeModuloShardingTableAlgorithm:
/** * 日期範圍查詢 */public class DateRangeModuloShardingTableAlgorithm implements RangeShardingAlgorithm<Date> { @Override public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Date> rangeShardingValue) { // 這裡可以處理日期欄位,避免多餘查詢 Range<Date> range = rangeShardingValue.getValueRange(); Integer start = null; if (range.hasLowerBound()) { Date lowerEndpoint = range.lowerEndpoint(); Calendar c = Calendar.getInstance(); c.setTime(lowerEndpoint); start = Calendar.YEAR; } Integer end = null; if (range.hasUpperBound()) { Date upperEndpoint = range.upperEndpoint(); Calendar c = Calendar.getInstance(); c.setTime(upperEndpoint); end = Calendar.YEAR; } if (null == start && null == end) { return collection; } List<String> list = new ArrayList<>(); for (String tableName : collection) { int suffix = Integer.parseInt(tableName.substring(tableName.lastIndexOf("_") + 1)); if (null != start && suffix < start) { continue; } if (null != end && suffix > end) { continue; } list.add(tableName); } return list; }}
如果日誌量大,還可以按季度分表,只要在方法中稍作修改,返回對應的表名即可。
關於 Sharding JDBC 的分片策略和分片演算法,可查閱官方文件。
注意事項使用分庫分表後要注意主鍵唯一,可使用雪花演算法生成主鍵使用 Sharding JDBC 查詢時儘量帶上分庫分表字段作為條件,避免全域性掃描主從模型中,事務中讀寫均用主庫某些sql語句不支援解析,如 distinct 和 group by 連用Sharding JDBC 4.x SQL 解析支援說明Sharding ProxySharding Proxy 定位為透明化的資料庫代理端,提供封裝了資料庫二進位制協議的服務端版本,用於完成對異構語言的支援。 目前先提供MySQL/PostgreSQL版本,它可以使用任何相容MySQL/PostgreSQL協議的訪問客戶端(如:MySQL Command Client, MySQL Workbench, Navicat等)操作資料,對DBA更加友好。
使用 Sharding JDBC 分庫分表後,數庫落在不同的資料庫和資料表中,使用 Sharding Proxy 作為代理,它會幫我們把上面的 t_user_0,t_user_1 … 聚合成一張 t_user 邏輯表。
Sharding Proxy 的安裝配置請參考官方文件。如果有自定義分片演算法,把程式碼打成JAR包放到 Sharding Proxy 解壓後的conf/lib目錄下。
實際使用中發現 Sharding Proxy 對連線的客戶端有版本校驗,比如我用 MySQL Workbench 8.0 無法連線 MySQL 5.7 的資料庫。可以使用 DBeaver 客戶端,手動指定與服務端版本對應的驅動。
Sharding UISharding UI是 ShardingSphere 的一個簡單而有用的web管理控制檯。它用於幫助使用者更簡單地使用 ShardingSphere 的相關功能,目前提供註冊中心管理、動態配置管理、資料庫編排等功能。
Sharding UI 和 Sharding Proxy 方便我們管理資料庫,在理解 Sharding JDBC 分庫分表後配置十分簡單,可參考 Sharding Sphere 4.x 相關文件
本文連結:https://engr-z.com/177.html