上一章已經講述分庫分表算法選型,本章主要講述分庫分表技術選型
文中關聯上一章,若下文出現提及其時,可以點擊 分庫分表算法方案與技術選型(一)
主要講述
- 框架比較
- sharding-jdbc、zdal 代碼實現樣例,如需源碼可在后文中查看
- 主鍵生成策略
可以按需閱讀文章
點贊再看,關注公眾號:【地藏思維】給大家分享互聯網場景設計與架構設計方案
掘金:地藏Kelvin https://juejin.im/user/5d67da8d6fb9a06aff5e85f7
常見框架
除了原生JDBC,網上常見分庫分表框架有:
當當網 sharding-jdbc
alibaba.cobar (是阿里巴巴(B2B)部門開發)
MyCAT(基於阿里開源的Cobar產品而研發)
螞蟻金服 ZDAL (開源)
蘑菇街 TSharding
當然除了這些,還有很多各自公司提出的框架,但是根據用戶量較高的為以上幾種。
其中自從出現基於cobar的MyCAT,zdal,也很少人用cobar了。ZDAL雖然也是開源,但是很少文章和使用反饋,不支持MongoDb,交流活躍度也比較低。
所以本次文章來比較一下活躍度較高的sharding-jdbc和MyCAT。
擴展閱讀:當當網做的不錯的,除了sharding-jdbc,還有elastic-job用於定時任務分片
對比概覽
主要指標 | Sharding-jdbc | Mycat | zdal |
---|---|---|---|
ORM支持 | 任意 | 任意 | 任意 |
事務 | 自帶弱XA、最大努力送達型柔性事務BASE | 自帶弱XA | 自帶弱XA、最大努力送達型柔性事務BASE |
分庫 | 支持 | 支持 | 支持 |
分庫 | 支持 | 不支持單庫分表 | 支持 |
開發 | 開發成本高,代碼入侵大 | 開發成本小,代碼入侵小 | 開發成本不算高配置明確 |
所屬公司 | 當當網 | 基於阿里Cobar二次開發,社區維護 | 螞蟻金服 |
數據庫支持 | 任意 | Oracle、 SQL Server、 Mysql、DB2、mongodb | 不支持mongodb |
活躍度 | 也有不少的企業在最近幾年新項目使用 | 社區活躍度很高,一些公司已在使用 | 活躍度低 |
監控 | 無 | 有 | |
讀寫分離 | 支持 | 支持 | |
資料 | 資料少、github、官網、網上討論貼 | 資料多,github、官網、Q群、書籍 | 少 |
運維 | 維護成本低 | 維護成本高 | 維護成本低 |
限制 | 部分JDBC方法不支持、SQL語句限制 | SQL語句限制 | |
連接池 | druid版本 | 無要求 | 無要求 |
配置難度 | 一般 | 復雜 | 比較簡單,讀寫分離、分開分表規則設置量少 |
關鍵指標對比
1.開發與運維成本
sharding-jdbc
- sharding-jdbc是一個輕量級框架,不是獨立運行中間件,以工程的依賴jar的形式提供功能,無需額外部署,可以理解為增強版JDBC驅動。
- 對運維、DBA人員無需感知代碼與分片策略規則,運維只需要維護執行建立表和數據的遷移。
- 相對Mycat這是sharding-jdbc的優勢,減少了部署成本以及DBA學習成本。
- 原理是通過規則改寫原sql,如select * from A 根據規則變成select * from A_01,運行執行sql時就會向mysql服務器傳select * from A_01指令。
MyCat
- 而MyCat並不是業務系統代碼里面的配置,而是獨立運行的中間件,所以配置都會交給DBA執行。
- 對於DBA來說,他是一個在mysql Server前,增加一層代理,mycat本身不存數據,數據是在后端的MySQL上存儲的,因此數據可靠性以及事務等都是MySQL保證的。
- 為了減少遷移數據的風險,在 上一章推薦的增量遷移算法方案(推薦大家閱讀)講述如何分片達到降低風險。
若用MyCat,DBA需要配置多次的增量分片規則,每配置一次則要重啟一次,才能達到一輪的數據遷移。實際上MyCat down掉的時系統都不能對數據庫查詢,實際依然對所有用戶有影響。 - 然而sharding-jdbc都在代碼實現路由規則,則可以減少DBA操作次數和系統重啟次數,進而減少影響用戶數。
推薦閱讀第一章的第五節才比較好理解上述3~4點 分庫分表算法方案與技術選型(一)
- proxy整合大數據思路,將 OLTP 和 OLAP 分離處理,可能會對大數據處理的系統比較適合,畢竟數據工作不一定有java后端系統。
該點總結:sharding-jdbc增量分片和增量遷移數據效果更佳,mycat比較適合大數據工作
備注:
sharding-jdbc增強了JDBC驅動部分功能,但同時也限制部分原生JDBC接口的使用。具體限制參考:
限制情況:http://dangdangdotcom.github.io/sharding-jdbc/01-start/limitations/ 這個文檔現在好像訪問不了
附:
官網文檔
官網源碼
2.分庫分表能力
- sharding-jdbc另一個優勢是他的分表能力,可以不需要分庫的情況下單庫分表。
- MyCAT不能單庫分多表,必須分庫,這樣就會造成讓DBA增加機器節點,即使不增加機器節點,也會在同一個機器上增加mysql server實例,若使用sharding-jdbc單庫分多表,則DBA只需要執行建立表語句即可。
3.事務
首先說說XA, XA 多階段提交的方式,雖然對分布式數據的完整性有比較好的保障,但會極大的降影響應用性能。
-
sharding-jdbc和mycat支持弱XA,弱 XA 就是分庫之后的數據庫各自負責自己事務的提交和回滾,沒有統一的調度器集中處理。這樣做的好處是天然就支持,對性能也沒有影響。但一旦出問題,比如兩個庫的數據都需要提交,一個提交成功,另一個提交時斷網導致失敗,則會發生數據不一致的問題,而且這種數據不一致是永久存在的。
-
柔性事務是對弱 XA 的有效補充。柔性事務類型很多。
Sharding-JDBC 主要實現的是最大努力送達型。即認為事務經過反復嘗試一定能夠成功。如果每次事務執行失敗,則記錄至事務庫,並通過異步的手段不斷的嘗試,直至事務成功(可以設置嘗試次數,如果嘗試太多仍然失敗則入庫並需要人工干預)。在嘗試的途中,數據會有一定時間的不一致,但最終是一致的。通過這種手段可以在性能不受影響的情況下犧牲強一致性,達到數據的最終一致性。最大努力送達型事務的缺點是假定事務一定是成功的,無法回滾,因此不夠靈活。
備注:
還有一種柔性事務類型是 TCC,即 Try Confirm Cancel。可以通過事務管理器控制事務的提交或回滾,更加接近原生事務,但仍然是最終一致性。其缺點是需要業務代碼自行實現 Try Confirm Cancel 的接口,對現有業務帶來一定沖擊。Sharding-JDBC 未對 TCC 的支持。
4.監控
為什么要監控,因為上述事務的弱XA、最大努力送達型,其實還是有概率失敗。
- MyCat就要監控頁面,監控MyCat與Mysql server的心跳,運維人員可以看到
- 而sharding-jdbc沒有監控事務是不是最終執行了,可能需要改寫源碼,如果有個分片沒執行成功就發一下短信、釘釘之類的。
MyCat監控配置樣例
5.語句限制
- sharding-jdbc分庫分表使用 like 查詢是有限制的。目前 Shariding-JDBC 不支持 like 語句中包含分片鍵,但不包含分片鍵的 like 語句可以正確執行。
至於 like 性能問題,是與數據庫相關的,Shariding-JDBC 僅僅是解析 SQL 以及路由至正確的數據源而已。
是否會查詢所有的庫和表是根據分片鍵決定的,如果 SQL 中不包括分片鍵,就會查詢所有庫和表,這個和是否有 like 沒有關系。 - MyCat沒有限制
6.比較螞蟻金服的zdal
相對zdal來說,sharding-jdbc的配置量差不多,但是sharding-jdbc提供了java、springboot、yml、spring命名空間方式,而且有官方網站和gitee網站維護。相對zdal用戶更加活躍。
Sharding-jdbc分庫分表整合mybatis-plus 開發樣例
代碼樣例具體描述,下述關鍵的開發點。
具體源碼請到我的gitee地址sharding-jdbc-example。
sharding-jdbc分片的開發主要幾個關鍵點:
0. 引入關鍵依賴 2019.10最新版4.0.0-RC2
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-namespace</artifactId>
<version>4.0.0-RC2</version>
</dependency>
- 在xml中配置基礎數據源、分庫分表的策略,其中DbShardingAlgorithm,TbShardingAlgorithm需要在java代碼里面實現。
<--分庫的規則對象類->
<bean id="preciseModuloDatabaseShardingAlgorithm" class="com.dizang.sharding.config.algorithm.DbShardingAlgorithm" />
<--分表規則對象類->
<bean id="preciseModuloTableShardingAlgorithm" class="com.dizang.sharding.config.algorithm.TbShardingAlgorithm" />
<--分庫根據的key->
<sharding:standard-strategy id="databaseStrategy" sharding-column="user_id" precise-algorithm-ref="preciseModuloDatabaseShardingAlgorithm" />
<--分庫根據的key->
<sharding:standard-strategy id="tableShardingStrategy" sharding-column="user_id" precise-algorithm-ref="preciseModuloTableShardingAlgorithm" />
<sharding:key-generator id="orderKeyGenerator" type="SNOWFLAKE" column="id" />
<sharding:data-source id="shardingDataSource">
<--分表數據源->
<sharding:sharding-rule data-source-names="ds0, ds1">
<sharding:table-rules>
<--邏輯表名->
<sharding:table-rule logic-table="t_user"
actual-data-nodes="ds$->{0..1}.t_user_$->{0..2}"
<--分庫分表邏輯bean->
database-strategy-ref="databaseStrategy"
table-strategy-ref="tableShardingStrategy"
key-generator-ref="orderKeyGenerator" />
</sharding:table-rules>
<--配置能適用規則的表->
<sharding:binding-table-rules>
<sharding:binding-table-rule logic-tables="t_user" />
</sharding:binding-table-rules>
<--配置不需要分庫分表的表->
<sharding:broadcast-table-rules>
<sharding:broadcast-table-rule table="t_order" />
</sharding:broadcast-table-rules>
</sharding:sharding-rule>
</sharding:data-source>
<--sqlSessionFactory配置shardingDataSource數據源->
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="shardingDataSource"/>
<property name="mapperLocations" value="classpath*:mapper/*.xml"/>
</bean>
- java代碼編寫分庫策略
需要繼承SingleKeyDatabaseShardingAlgorithm分開規則類,重寫equal等於、大於、小於時的路由規則
public class DbShardingAlgorithm implements PreciseShardingAlgorithm<Long>{
@Override
public String doSharding(Collection<String> databaseNames, PreciseShardingValue<Long> shardingValue) {
for (String each : databaseNames) {
if (each.endsWith(shardingValue.getValue() % 2 + "")) {
return each;
}
}
return null;
}
}
- java代碼編寫分表策略
需要繼承SingleKeyTableShardingAlgorithm分開規則類,重寫equal等於、大於、小於時的路由規則
public class TbShardingAlgorithm implements PreciseShardingAlgorithm<Long>{
@Override
public String doSharding(Collection<String> tableNames, PreciseShardingValue<Long> shardingValue) {
for (String each : tableNames) {
if (each.endsWith(shardingValue.getValue() % 2 + "")) {
return each;
}
}
// return shardingValue.getLogicTableName()+(shardingValue.getValue() % 2);
throw new UnsupportedOperationException();
}
}
zdal具體代碼實現推薦閱讀
歡迎關注
我的公眾號 :地藏思維
我的Gitee: 地藏Kelvin https://gitee.com/dizang-kelvin
推薦閱讀sharding-jdbc源碼: