JAVA 關(guān)于分庫(kù)分表的學(xué)習(xí)和DEMO

在考慮分表分庫(kù)之前還有不少的優(yōu)化手段
第一 優(yōu)化你的sql和索引
第二 加緩存,memcached,redis
第三 數(shù)據(jù)庫(kù)讀寫分離

源碼地址
https://github.com/LH-0811/sharding-sphere-demo
如果有錯(cuò)誤 或者不足的地方 希望大家能指出謝謝蚓庭!

關(guān)于這個(gè)方面的學(xué)習(xí)为严,只是以備不時(shí)之需趟脂。系統(tǒng)的過度設(shè)計(jì)是一個(gè)浪費(fèi)精力和資源的事情育八。系統(tǒng)應(yīng)當(dāng)隨著數(shù)據(jù)和業(yè)務(wù)的增多逐步生長(zhǎng)施蜜。但是要領(lǐng)先業(yè)務(wù)替裆。以備業(yè)務(wù)的爆發(fā)式增長(zhǎng)

1、關(guān)于分表(這里指的肯定是水平分表)

在我的思路中 如果要實(shí)現(xiàn)分表填抬,首先要考慮每條記錄以什么樣的規(guī)則來區(qū)分放到哪一張表中烛芬。最直接的方式就是以某個(gè)字段或者某些字段通過某種算法來得出一個(gè)結(jié)果,對(duì)應(yīng)到一張表中痴奏。

假設(shè)user 表蛀骇。字段 有 id(int) , name(varchar) , age(int)

1.1增加數(shù)據(jù)

如果就以id作為分表因子分兩張表读拆。那可以定義規(guī)則 id % 2 得出結(jié)果 0 或者1 這里就可以對(duì)應(yīng)兩張表 user_0 user_1 然后對(duì)應(yīng)的將這條數(shù)據(jù)插入到表中。
假設(shè)id 是 1
1%2 = 1
所以要執(zhí)行的sql

INSERT INTO `user_1` VALUES (1, 'test1', 10);
1.2查詢數(shù)據(jù)

如果要做數(shù)據(jù)查詢 鸵闪。依然還是要確定要操作那張表檐晕。
依然以 id%2 的結(jié)果來確定操作那個(gè)表
比如 id = 2
2 % 2 = 0
所以執(zhí)行的sql

select * from 'user_0' where id = 2;

2、關(guān)于分庫(kù)

這里假設(shè)有兩個(gè) 庫(kù) db0 db1
之后要做的事情就是 定義分庫(kù)規(guī)則蚌讼。

借鑒分表的思路 就可以用id做因子 然后 id%2 做規(guī)則確定操作那個(gè)數(shù)據(jù)庫(kù)辟灰。
如果數(shù)據(jù)中的user表依然要分 user_0 user_1
那可以制定 user.age 作為分表因子 user.age%2 做規(guī)則。具體的思路跟上面的分表一樣篡石。

分庫(kù)的時(shí)候 就是需要兩個(gè)規(guī)則 來確定需要操作的 庫(kù) 和 表芥喇。

3、關(guān)于分表分庫(kù)的應(yīng)用

我這個(gè)菜雞凰萨,假設(shè)要實(shí)踐继控,會(huì)有不小的麻煩 首先一點(diǎn)就是代碼的入侵太嚴(yán)重械馆。之前使用的

select * from user where id = ?;

就要修改為

if(id%2 == 0){
select * from user_0 where id = ?;
}else{
select * from user_1 where id = ?;
}

而且 如果某一天 變成了分三張表就要對(duì)應(yīng)的修改代碼。這里的舉例是最簡(jiǎn)單的語句武通。項(xiàng)目中肯定不會(huì)這么簡(jiǎn)單霹崎。所以一旦修改了分表規(guī)則,那將是噩夢(mèng)般的工作量冶忱。而且ORM框架幾乎就是不可用了尾菇。

4、關(guān)于分表分庫(kù)的應(yīng)用 中間件的查找和對(duì)比

網(wǎng)上關(guān)于這方面的資料我找了些囚枪。引用https://www.cnblogs.com/wangzhongqiu/p/7100332.html

反正我沒啥耐心意義去自己測(cè)試這些中間件派诬。基本上選定了兩個(gè)方案链沼。mycat默赂,Sharding-JDBC

1>Cobar 是提供關(guān)系型數(shù)據(jù)庫(kù)(MySQL)分布式服務(wù)的中間件,它可以讓傳統(tǒng)的數(shù)據(jù)庫(kù)得到良好的線性擴(kuò)展忆植,并看上去還是一個(gè)數(shù)據(jù)庫(kù)放可,對(duì)應(yīng)用保持透明。

Cobar以Proxy的形式位于前臺(tái)應(yīng)用和實(shí)際數(shù)據(jù)庫(kù)之間朝刊,對(duì)前臺(tái)的開放的接口是MySQL通信協(xié)議耀里,將前臺(tái)SQL語句變更并按照數(shù)據(jù)分布規(guī)則發(fā)到合適的后臺(tái)數(shù)據(jù)分庫(kù),再合并返回結(jié)果拾氓,模擬單庫(kù)下的數(shù)據(jù)庫(kù)行為冯挎。

Cobar屬于中間層方案,在應(yīng)用程序和MySQL之間搭建一層Proxy咙鞍。中間層介于應(yīng)用程序與數(shù)據(jù)庫(kù)間房官,需要做一次轉(zhuǎn)發(fā),而基于JDBC協(xié)議并無額外轉(zhuǎn)發(fā)续滋,直接由應(yīng)用程序連接數(shù)據(jù)庫(kù)翰守,

性能上有些許優(yōu)勢(shì)摊灭。這里并非說明中間層一定不如客戶端直連蔬将,除了性能崇堰,需要考慮的因素還有很多颤绕,中間層更便于實(shí)現(xiàn)監(jiān)控酌儒、數(shù)據(jù)遷移罢防、連接管理等功能叨襟。

Cobar屬于阿里B2B事業(yè)群床蜘,始于2008年粥诫,在阿里服役3年多油航,接管3000+個(gè)MySQL數(shù)據(jù)庫(kù)的schema,集群日處理在線SQL請(qǐng)求50億次以上。

由于Cobar發(fā)起人的離職怀浆,Cobar停止維護(hù)谊囚。后續(xù)的類似中間件怕享,比如MyCAT建立于Cobar之上,包括現(xiàn)在阿里服役的RDRS其中也復(fù)用了Cobar-Proxy的相關(guān)代碼秒啦。

2>MyCAT是社區(qū)愛好者在阿里cobar基礎(chǔ)上進(jìn)行二次開發(fā)熬粗,解決了cobar當(dāng)時(shí)存 在的一些問題,并且加入了許多新的功能在其中余境。目前MyCAT社區(qū)活 躍度很高驻呐,

目前已經(jīng)有一些公司在使用MyCAT》祭矗總體來說支持度比 較高含末,也會(huì)一直維護(hù)下去,發(fā)展到目前的版本即舌,已經(jīng)不是一個(gè)單純的MySQL代理了佣盒,

它的后端可以支持MySQL, SQL Server, Oracle, [DB2](http://www.2cto.com/database/DB2/), PostgreSQL等主流數(shù)據(jù)庫(kù),也支持MongoDB這種新型NoSQL方式的存儲(chǔ)顽聂,未來還會(huì)支持更多類型的存儲(chǔ)肥惭。

MyCAT是一個(gè)強(qiáng)大的數(shù)據(jù)庫(kù)中間件,不僅僅可以用作讀寫分離紊搪,以及分表分庫(kù)蜜葱、容災(zāi)管理,而且可以用于多租戶應(yīng)用開發(fā)耀石、云平臺(tái)基礎(chǔ)設(shè)施牵囤,讓你的架構(gòu)具備很強(qiáng)的適應(yīng)性和靈活性,

借助于即將發(fā)布的MyCAT只能優(yōu)化模塊滞伟,系統(tǒng)的數(shù)據(jù)訪問瓶頸和熱點(diǎn)一目了然揭鳞,根據(jù)這些統(tǒng)計(jì)分析數(shù)據(jù),你可以自動(dòng)或手工調(diào)整后端存儲(chǔ)梆奈,將不同的表隱射到不同存儲(chǔ)引擎上野崇,而整個(gè)應(yīng)用的代碼一行也不用改變。

MyCAT是在Cobar基礎(chǔ)上發(fā)展的版本亩钟,兩個(gè)顯著提高:后端由BIO改為NIO舞骆,并發(fā)量有大幅提高; 增加了對(duì)Order By, Group By, Limit等聚合功能

(雖然Cobar也可以支持Order By, Group By, Limit語法径荔,但是結(jié)果沒有進(jìn)行聚合,只是簡(jiǎn)單返回給前端脆霎,聚合功能還是需要業(yè)務(wù)系統(tǒng)自己完成)

3>TDDL是Tabao根據(jù)自己的業(yè)務(wù)特點(diǎn)開發(fā)了(Tabao Distributed Data Layer, 外號(hào):頭都大了)总处。主要解決了分庫(kù)分表對(duì)應(yīng)用的透明化以及異構(gòu)數(shù)據(jù)庫(kù)之間的數(shù)據(jù)復(fù)制,

它是一個(gè)基于集中式配置的jdbc datasourcce實(shí)現(xiàn)睛蛛,具有主備鹦马,讀寫分離胧谈,動(dòng)態(tài)數(shù)據(jù)庫(kù)配置等功能。

TDDL并非獨(dú)立的中間件荸频,只能算作中間層菱肖,處于業(yè)務(wù)層和JDBC層中間,是以Jar包方式提供給應(yīng)用調(diào)用旭从,屬于JDBC Shard的思想稳强。

TDDL源碼:[https://github.com/alibaba/tb_tddl](https://github.com/alibaba/tb_tddl) 
TDDL復(fù)雜度相對(duì)較高。當(dāng)前公布的文檔較少和悦,只開源動(dòng)態(tài)數(shù)據(jù)源退疫,分表分庫(kù)部分還未開源,還需要依賴diamond鸽素,不推薦使用褒繁。

4>DRDS是阿里巴巴自主研發(fā)的分布式數(shù)據(jù)庫(kù)服務(wù)(此項(xiàng)目不開源),DRDS脫胎于阿里巴巴開源的Cobar分布式數(shù)據(jù)庫(kù)引擎,吸收了Cobar核心的Cobar-Proxy[源碼](http://www.2cto.com/ym/)馍忽,

實(shí)現(xiàn)了一套獨(dú)立的類似MySQL-Proxy協(xié)議的解析端棒坏,能夠?qū)魅氲腟QL進(jìn)行解析和處理,對(duì)應(yīng)用程序屏蔽各種復(fù)雜的底層DB拓?fù)浣Y(jié)構(gòu)遭笋,獲得單機(jī)數(shù)據(jù)庫(kù)一樣的使用體驗(yàn)坝冕,

同時(shí)借鑒了淘寶TDDL豐富的分布式數(shù)據(jù)庫(kù)實(shí)踐經(jīng)驗(yàn),實(shí)現(xiàn)了對(duì)分布式Join支持坐梯,SUM/MAX/COUNT/AVG等聚合函數(shù)支持以及排序等函數(shù)支持徽诲,

通過異構(gòu)索引、小表廣播等解決分布式數(shù)據(jù)庫(kù)使用場(chǎng)景下衍生出的一系列問題吵血,最終形成了完整的分布式數(shù)據(jù)庫(kù)方案谎替。

5>Atlas是一個(gè)位于應(yīng)用程序與MySQL之間的*基于MySQL協(xié)議的數(shù)據(jù)中間層項(xiàng)目*,**它是在mysql-proxy 0.8.2版本上對(duì)其進(jìn)行優(yōu)化蹋辅,**360團(tuán)隊(duì)基于mysql proxy 把lua用C改寫钱贯,****

*它實(shí)現(xiàn)了MySQL的客戶端和服務(wù)端協(xié)議,**作為服務(wù)端與應(yīng)用程序通訊侦另,同時(shí)作為客戶端與MySQL通訊秩命。它對(duì)應(yīng)用程序屏蔽了DB的細(xì)節(jié)。*

*Altas不能實(shí)現(xiàn)分布式分表褒傅,所有的字表必須在同一臺(tái)DB的同一個(gè)DataBase里且所有的字表必須實(shí)現(xiàn)建好弃锐,Altas沒有自動(dòng)建表的功能。*

*原有版本是不支持分庫(kù)分表殿托, 目前已經(jīng)放出了分庫(kù)分表版本霹菊。在網(wǎng)上看到一些朋友經(jīng)常說在高并 發(fā)下會(huì)經(jīng)常掛掉,如果大家要使用需要提前做好測(cè)試支竹。*

6>DBProxy是美團(tuán)點(diǎn)評(píng)DBA團(tuán)隊(duì)針對(duì)公司內(nèi)部需求旋廷,在奇虎360公司開源的Atlas做了很多改進(jìn)工作鸠按,形成了新的高可靠、高可用企業(yè)級(jí)數(shù)據(jù)庫(kù)中間件

其特性主要有:讀寫分離饶碘、負(fù)載均衡目尖、支持分表、IP過濾扎运、sql語句黑名單瑟曲、DBA平滑下線DB、從庫(kù)流量配置绪囱、動(dòng)態(tài)加載配置項(xiàng)

項(xiàng)目的Github地址是[https://github.com/Meituan-Dianping/DBProxy](https://github.com/Meituan-Dianping/DBProxy)

7>sharding-JDBC是當(dāng)當(dāng)應(yīng)用框架ddframe中测蹲,從關(guān)系型數(shù)據(jù)庫(kù)模塊dd-rdb中分離出來的數(shù)據(jù)庫(kù)水平分片框架,實(shí)現(xiàn)透明化數(shù)據(jù)庫(kù)分庫(kù)分表訪問鬼吵。

Sharding-JDBC是繼dubbox和elastic-job之后扣甲,ddframe系列開源的第3個(gè)項(xiàng)目。

Sharding-JDBC直接封裝JDBC API齿椅,可以理解為增強(qiáng)版的JDBC驅(qū)動(dòng)琉挖,舊代碼遷移成本幾乎為零:

*   可適用于任何基于Java的ORM框架,如JPA涣脚、Hibernate示辈、Mybatis、Spring JDBC Template或直接使用JDBC遣蚀。
*   可基于任何第三方的數(shù)據(jù)庫(kù)連接池矾麻,如DBCP、C3P0芭梯、 BoneCP险耀、Druid等。
*   理論上可支持任意實(shí)現(xiàn)JDBC規(guī)范的數(shù)據(jù)庫(kù)玖喘。雖然目前僅支持MySQL甩牺,但已有支持Oracle、SQLServer等數(shù)據(jù)庫(kù)的計(jì)劃累奈。

Sharding-JDBC定位為輕量Java框架贬派,使用客戶端直連數(shù)據(jù)庫(kù),以jar包形式提供服務(wù)澎媒,無proxy代理層搞乏,無需額外部署,無其他依賴戒努,DBA也無需改變?cè)械倪\(yùn)維方式查描。

Sharding-JDBC分片策略靈活,可支持等號(hào)、between冬三、in等多維度分片,也可支持多分片鍵缘缚。

SQL解析功能完善勾笆,支持聚合、分組桥滨、排序窝爪、limit、or等查詢齐媒,并支持Binding Table以及笛卡爾積表查詢蒲每。

5、mycat的調(diào)研喻括。邀杏。

http://www.mycat.io/
具體不多做評(píng)價(jià)了 這個(gè)中間件確實(shí)是厲害。

6唬血、Sharding-JDBC的調(diào)研

最后 選擇了Sharding-JDBC望蜡。為啥不選mycat 我也想用啊 不太敢。怕萬一哪天就商業(yè)化了拷恨。

最終 Sharding-JDBC 其實(shí)可以滿足基本的需要 量級(jí)也比較輕

7脖律、Sharding-JDBC DEMO

源碼地址
https://github.com/LH-0811/sharding-sphere-demo

準(zhǔn)備兩個(gè)數(shù)據(jù)庫(kù)


image.png

這里為了做一些測(cè)試 在db1中添加一個(gè)分?jǐn)?shù)表

/*
 Navicat Premium Data Transfer

 Source Server         : 192.168.199.127_3316
 Source Server Type    : MySQL
 Source Server Version : 50724
 Source Host           : 192.168.199.127:3316
 Source Schema         : db0

 Target Server Type    : MySQL
 Target Server Version : 50724
 File Encoding         : 65001

 Date: 01/06/2019 10:17:49
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user_0
-- ----------------------------
DROP TABLE IF EXISTS `user_0`;
CREATE TABLE `user_0` (
  `id` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;



-- ----------------------------
-- Table structure for user_1
-- ----------------------------
DROP TABLE IF EXISTS `user_1`;
CREATE TABLE `user_1` (
  `id` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for score
-- ----------------------------
DROP TABLE IF EXISTS `score`;
CREATE TABLE `score` (
  `id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `score` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

-- ----------------------------
-- Table structure for user_0
-- ----------------------------
DROP TABLE IF EXISTS `user_0`;
CREATE TABLE `user_0` (
  `id` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Table structure for user_1
-- ----------------------------
DROP TABLE IF EXISTS `user_1`;
CREATE TABLE `user_1` (
  `id` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

創(chuàng)建一個(gè)springboot程序 然后修改配置文件
,這個(gè)配置文件的來源是https://mp.weixin.qq.com/s/M0e5u8V_c2sv6aYJHl0Y6Q

# 數(shù)據(jù)源 db0,db1
sharding.jdbc.datasource.names=db0,db1
# 第一個(gè)數(shù)據(jù)庫(kù)
sharding.jdbc.datasource.db0.type=com.zaxxer.hikari.HikariDataSource
sharding.jdbc.datasource.db0.driver-class-name=com.mysql.jdbc.Driver
sharding.jdbc.datasource.db0.jdbc-url=jdbc:mysql://192.168.199.127:3316/db0?characterEncoding=utf-8
sharding.jdbc.datasource.db0.username=root
sharding.jdbc.datasource.db0.password=123456

# 第二個(gè)數(shù)據(jù)庫(kù)
sharding.jdbc.datasource.db1.type=com.zaxxer.hikari.HikariDataSource
sharding.jdbc.datasource.db1.driver-class-name=com.mysql.jdbc.Driver
sharding.jdbc.datasource.db1.jdbc-url=jdbc:mysql://192.168.199.127:3326/db1?characterEncoding=utf-8
sharding.jdbc.datasource.db1.username=root
sharding.jdbc.datasource.db1.password=123456

# 水平拆分的數(shù)據(jù)庫(kù)(表) 配置分庫(kù) + 分表策略 行表達(dá)式分片策略
# 分庫(kù)策略
sharding.jdbc.config.sharding.default-database-strategy.inline.sharding-column=id
sharding.jdbc.config.sharding.default-database-strategy.inline.algorithm-expression=db$->{id % 2}

# 分表策略 其中user為邏輯表 分表主要取決于age行
sharding.jdbc.config.sharding.tables.user.actual-data-nodes=db$->{0..1}.user_$->{0..1}
sharding.jdbc.config.sharding.tables.user.table-strategy.inline.sharding-column=age
# 分片算法表達(dá)式
sharding.jdbc.config.sharding.tables.user.table-strategy.inline.algorithm-expression=user_$->{age % 2}

sharding.jdbc.config.sharding.tables.score.actual-data-nodes=db1.score


# 主鍵 UUID 18位數(shù) 如果是分布式還要進(jìn)行一個(gè)設(shè)置 防止主鍵重復(fù)
#sharding.jdbc.config.sharding.tables.user.key-generator-column-name=id

# 打印執(zhí)行的數(shù)據(jù)庫(kù)以及語句
sharding.jdbc.config.props..sql.show=true
spring.main.allow-bean-definition-overriding=true

這邊的配置文件 幾個(gè)注意的地方

分庫(kù)配置

# 水平拆分的數(shù)據(jù)庫(kù)(表) 配置分庫(kù) + 分表策略 行表達(dá)式分片策略
# 分庫(kù)策略 指定分庫(kù)的因子 這里使用id來分庫(kù)
sharding.jdbc.config.sharding.default-database-strategy.inline.sharding-column=id
# 這里是通過id來計(jì)算 應(yīng)該操作那個(gè)數(shù)據(jù)庫(kù)
sharding.jdbc.config.sharding.default-database-strategy.inline.algorithm-expression=db$->{id % 2}


分表配置

# 分表策略 其中user為邏輯表 分表主要取決于age行

#這里是在分庫(kù)的基礎(chǔ)上 添加了分表 
#sharding.jdbc.config.sharding.tables.user 是指定邏輯表user 對(duì)應(yīng)的物理表 在那個(gè)數(shù)據(jù)庫(kù)中的那幾張表
sharding.jdbc.config.sharding.tables.user.actual-data-nodes=db$->{0..1}.user_$->{0..1}

# 這里是指定user表的分表 是通過那個(gè)因子來計(jì)算
sharding.jdbc.config.sharding.tables.user.table-strategy.inline.sharding-column=age
# 分片算法表達(dá)式 
sharding.jdbc.config.sharding.tables.user.table-strategy.inline.algorithm-expression=user_$->{age % 2}

# 如果不指定邏輯表 score 對(duì)應(yīng)在db1中的score sharding.jdbc中間件會(huì)默認(rèn)找db0 db1 中的score 但是db0中并沒有創(chuàng)建這個(gè)表 所以這里就指定他 不找db0中的表
sharding.jdbc.config.sharding.tables.score.actual-data-nodes=db1.score

這里貼一下pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lh.sharding-sphere</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sharding-sphere-demo</name>
    <description>sharding-sphere jdbc 分庫(kù)分表測(cè)試demo</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


        <!-- https://mvnrepository.com/artifact/tk.mybatis/mapper-spring-boot-starter -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.1.5</version>
        </dependency>


        <!-- for spring boot -->

        <dependency>
            <groupId>io.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>3.1.0</version>
        </dependency>

        <!-- for spring namespace -->

        <dependency>
            <groupId>io.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-namespace</artifactId>
            <version>3.1.0</version>
        </dependency>

        <!--shardingsphere end-->


        <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper-spring-boot-starter -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.10</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Model

@Data
@Table(name = "user")
@NoArgsConstructor
@AllArgsConstructor
public class User {

    @Id
    @KeySql(useGeneratedKeys = true)
    private Integer id;

    private String name;

    private Integer age;

}


@Data
@Table(name = "score")
@NoArgsConstructor
@AllArgsConstructor
public class Score {

    @Id
    @KeySql(useGeneratedKeys = true)
    private Integer id;

    private Integer userId;

    private Integer score;

}

Dao

package com.lh.shardingsphere.demo.dao;


import com.lh.shardingsphere.demo.model.Score;
import tk.mybatis.mapper.common.BaseMapper;
import tk.mybatis.mapper.common.ExampleMapper;

public interface ScoreDao extends BaseMapper<Score>, ExampleMapper<Score> {
}

package com.lh.shardingsphere.demo.dao;


import com.lh.shardingsphere.demo.model.User;
import com.lh.shardingsphere.demo.vo.UserScoreVo;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import tk.mybatis.mapper.common.BaseMapper;
import tk.mybatis.mapper.common.ExampleMapper;

public interface UserDao extends BaseMapper<User>, ExampleMapper<User> {

//     這是個(gè)反面的錯(cuò)誤M笾丁小泉!。冕杠。本來一條sql就只能在一個(gè)session中執(zhí)行微姊,
//    只有在兩張表在同一個(gè)數(shù)據(jù)庫(kù)中存在的時(shí)候才可以通過join的方式來獲取數(shù)據(jù)
//
//    @Select("SELECT \n" +
//            "u.id as userId,\n" +
//            "s.id as scoreId,\n" +
//            "u.name as name,\n" +
//            "s.score as score\n" +
//            "FROM \n" +
//            "user u \n" +
//            "LEFT JOIN \n" +
//            "score s \n" +
//            "ON u.id = s.user_id \n" +
//            "WHERE u.id = ${userId}")
    @Select("SELECT \n" +
            "u.id as userId,\n" +
            "s.id as scoreId,\n" +
            "u.name as name,\n" +
            "s.score as score\n" +
            "FROM\n" +
            "user_0 u,score s\n" +
            "WHERE u.id = s.user_id AND u.id = ${userId}")
    UserScoreVo selectScoreByUserId(@Param("userId") Integer userId);
}

Service

package com.lh.shardingsphere.demo.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.lh.shardingsphere.demo.dao.ScoreDao;
import com.lh.shardingsphere.demo.dao.UserDao;
import com.lh.shardingsphere.demo.model.Score;
import com.lh.shardingsphere.demo.model.User;
import com.lh.shardingsphere.demo.vo.UserScoreVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

import java.util.List;

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private ScoreDao scoreDao;


    public void insertUser(User user) {
        userDao.insert(user);
    }


    public User selectUserById(Integer userId) {
        return userDao.selectByPrimaryKey(userId);
    }


    public List<User> selectUserList() {
        Example example = new Example(User.class);
        example.setOrderByClause(" id asc ");
        return userDao.selectByExample(example);
    }


    public List<User> selectByIdAndAge(Integer id, Integer age) {
        User user = new User();
        user.setAge(age);
        user.setId(id);
        return userDao.select(user);
    }

    public List<User> selectByAge(Integer age) {
        User user = new User();
        user.setAge(age);
        return userDao.select(user);
    }

    public List<User> selectByName(String name) {
        User user = new User();
        user.setName(name);
        return userDao.select(user);
    }

    public User selectByOneName(String name) {
        User user = new User();
        user.setName(name);
        return userDao.selectOne(user);
    }

    public List<User> selectByUser(User user) {
        return userDao.select(user);
    }


    public PageInfo<User> pageUser(Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        Example example = new Example(User.class);
        example.setOrderByClause(" id asc ");
        example.createCriteria().andLike("name", "%1%");
        List<User> users = userDao.selectByExample(example);
        PageInfo<User> pageInfo = new PageInfo<>(users);
        PageHelper.clearPage();
        return pageInfo;
    }


    public UserScoreVo selectUserScoreByUserId(Integer userId) {

        User user = userDao.selectByPrimaryKey(userId);

        if (user != null) {
            Score score = new Score();
            score.setUserId(userId);
            Score score1 = scoreDao.selectOne(score);

            UserScoreVo userScoreVo = new UserScoreVo();
            userScoreVo.setUserId(user.getId());
            userScoreVo.setScoreId(score1.getId());
            userScoreVo.setName(user.getName());
            userScoreVo.setScore(score1.getScore());
            return userScoreVo;
        } else {
            return null;
        }


    }

    public List<Score> getUserScore(Integer userId) {
        Score score = new Score();
        score.setUserId(userId);
        return scoreDao.select(score);
    }
}


8、demo的測(cè)試

package com.lh.shardingsphere.demo.service;

import com.github.pagehelper.PageInfo;
import com.lh.shardingsphere.demo.dao.ScoreDao;
import com.lh.shardingsphere.demo.dao.UserDao;
import com.lh.shardingsphere.demo.model.Score;
import com.lh.shardingsphere.demo.model.User;
import com.lh.shardingsphere.demo.vo.UserScoreVo;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {


    @Autowired
    private UserService userService;

    @Autowired
    private UserDao userDao;

    @Autowired
    private ScoreDao scoreDao;

    // 插入 方法拌汇。通過打印的sql執(zhí)行日志 可以看出
    // id為基數(shù)  分配得到 db1 偶數(shù)分配到 db0
    // user 中 age 奇數(shù)分配到 user_1  偶數(shù)分配到user_0
    @Test
    public void insertUser() {


        for (int i = 1; i < 50;i++){
            User user = new User(i,"test"+i,i+11);
            userService.insertUser(user);
        }

        for (int i = 50; i < 100;i++){
            User user = new User(i,"test"+i,i+10);
            userService.insertUser(user);
        }
    }

    // 通過id查詢數(shù)據(jù) 這里也是通過id 確定了數(shù)據(jù)庫(kù)
    @Test
    public void selectUserById() {


        User user1 = userService.selectUserById(11);
        log.info(user1.toString());
        User user2 = userService.selectUserById(12);
        log.info(user2.toString());
    }

    // 查詢列表 這里是每個(gè)數(shù)據(jù)庫(kù)中每個(gè)表返回值的集合
    // 從結(jié)果看出 排序是生效的
    @Test
    public void selectUserList() {
        List<User> users = userService.selectUserList();
        log.info(users.toString());
    }


    // 這里通過 age確定了 是哪張表 但是不確定庫(kù) 所以兩個(gè)庫(kù)都執(zhí)行了
    @Test
    public void selectByAge(){
        List<User> users = userService.selectByAge(12);
        log.info(users.toString());
        List<User> users1 = userService.selectByAge(13);
        log.info(users1.toString());
    }

    //這里通過 id和age 確定了 庫(kù)和表 所以就
    @Test
    public void  selectByIdAndAge(){
        List<User> users = userService.selectByIdAndAge(1, 12);
        log.info(users.toString());
    }


    //這里 因?yàn)闆]有指定 id 和 age 所以 是每個(gè)庫(kù)的每個(gè)表中搜索結(jié)果的集合
    @Test
    public void  selectByName(){
        List<User> test33 = userService.selectByName("test33");
        log.info(test33.toString());
    }

    //這里 如果在數(shù)據(jù)庫(kù)中每張表的數(shù)據(jù)不重復(fù)的情況下 可以正常執(zhí)行
    //如果 任意兩個(gè)表中 通過都可以通過name獲取到數(shù)據(jù) 就會(huì)報(bào)錯(cuò) 跟不分庫(kù)時(shí)一樣
    @Test
    public void selectByOneName(){
        User user = userService.selectByOneName("test33");
        log.info(user.toString());
    }




    // 這里分頁(yè)正常進(jìn)行 條件查詢正常進(jìn)行
    @Test
    public void pageUser(){
        PageInfo<User> pageInfo = userService.pageUser(1, 10);
        for (User user : pageInfo.getList()) {
            log.info(user.getId()+"");
        }
        log.info(pageInfo.toString());
    }




    @Test
    public void selectUserScoreByUserId() {
//        UserScoreVo userScoreVo1 = userService.selectUserScoreByUserId(1);
//        log.info(userScoreVo1.toString());
        UserScoreVo userScoreVo2 = userService.selectUserScoreByUserId(2);
        log.info(userScoreVo2.toString());
    }

    @Test
    public void selectScoreByUserId(){
        List<Score> userScore = userService.getUserScore(1);
        log.info(userScore.toString());
    }


    //這里的事務(wù)在數(shù)據(jù)庫(kù)中正常進(jìn)行 如果拋出異常 數(shù)據(jù)會(huì)回滾數(shù)據(jù)柒桑,即使是不在一個(gè)數(shù)據(jù)庫(kù)中也會(huì)分別進(jìn)行
    @Test
    @Transactional(rollbackFor = Exception.class)
    public void testSingleTableTransactional() throws Exception{
        User user = new User(1,"test111111",2222);
        userDao.updateByPrimaryKey(user);
        throw  new Exception("異常");
    }



    @Test
    @Transactional(rollbackFor = Exception.class)
    public void testTwoTableTransactional() throws Exception{
        User user = new User(2,"test111111",2222);
        userDao.updateByPrimaryKey(user);
        Score score = new Score();
        score.setUserId(1);
        score.setId(1);
        score.setScore(10000);
        scoreDao.updateByPrimaryKey(score);
        throw  new Exception("異常");
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市噪舀,隨后出現(xiàn)的幾起案子魁淳,更是在濱河造成了極大的恐慌,老刑警劉巖与倡,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件界逛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡纺座,警方通過查閱死者的電腦和手機(jī)息拜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人少欺,你說我怎么就攤上這事喳瓣。” “怎么了赞别?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵畏陕,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我仿滔,道長(zhǎng)惠毁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任崎页,我火速辦了婚禮鞠绰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘飒焦。我一直安慰自己蜈膨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布荒给。 她就那樣靜靜地躺著丈挟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪志电。 梳的紋絲不亂的頭發(fā)上曙咽,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音挑辆,去河邊找鬼例朱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛鱼蝉,可吹牛的內(nèi)容都是我干的洒嗤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼魁亦,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼渔隶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起洁奈,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤间唉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后利术,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呈野,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年印叁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了被冒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片军掂。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖昨悼,靈堂內(nèi)的尸體忽然破棺而出蝗锥,到底是詐尸還是另有隱情,我是刑警寧澤幔戏,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布玛追,位于F島的核電站,受9級(jí)特大地震影響闲延,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜韩玩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一垒玲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧找颓,春花似錦合愈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至彪蓬,卻和暖如春寸莫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背档冬。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工膘茎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人酷誓。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓披坏,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親盐数。 傳聞我的和親對(duì)象是個(gè)殘疾皇子棒拂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容