在考慮分表分庫(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ù)
這里為了做一些測(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("異常");
}
}