分庫分表之第五篇

9.案例

9.1.需求描述

電商平臺商品列表展現,每一個列表項中除了包含商品基本信息、商品描述信息以外,還包括了商品所屬的店鋪信息,以下 :
在這裏插入圖片描述
本案例實現功能以下:
一、添加商品
二、商品分頁查詢
三、商品統計java

9.2.數據庫設計

數據庫設計以下,其中商品與店鋪信息之間進行了垂直分庫,分爲了PRODUCT_DB(商品庫)和STORE_DB(店鋪庫);商品信息還進行了垂直分表,分爲了商品基本信息(product_info)和商品描述信息(product_descript),地理區域信息(region)做爲公共表,冗餘在兩庫中 :
在這裏插入圖片描述
考慮到商品信息的數據增加性,對PRODUCT_DB(商品庫)進行了水平分庫,分片鍵使用店鋪id,分片策略爲店鋪 ID%2 + 1,所以商品描述信息對所屬店鋪ID進行了冗餘;
對商品基本信息(product_info)和商品描述信息(product_descript)進行水平分表,分片鍵使用商品id,分片策略爲 商品ID%2 + 1,並將爲這兩個表設置爲綁定表,避免笛卡爾積join;
爲避免主鍵衝突,ID生成策略採用雪花算法來生成全局惟一ID,最終數據庫設計爲下圖:
在這裏插入圖片描述
要求使用讀寫分離來提高性能,可用性。node

9.3.環境說明

  • 操做系統 :win10
  • 數據庫 :MySQL-5.7.25
  • JDK :64位 jdk1.8.0_201
  • 應用框架 :spring-bbot-2.1.3.RELEASE,MyBatis3.5.0
  • Sharding-JDBC :sharding-jdbc-spring-boot-starter-4.0.0-RC1

9.4.環境準備

9.4.1.mysql主從同步(windows)

參考讀寫分離章節,對如下庫進行主從同步配置 :mysql

# 設置須要同步的數據庫 
 binlog‐do‐db=store_db binlog‐do‐db=product_db_1 binlog‐do‐db=product_db_2 

9.4.2.初始化數據庫

建立store_db數據庫,並執行如下腳本建立表 :算法

DROP TABLE IF EXISTS `region`; CREATE TABLE `region` ( `id` bigint(20) NOT NULL COMMENT 'id', `region_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地理區域編碼', `region_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地理區域名稱', `level` tinyint(1) NULL DEFAULT NULL COMMENT '地理區域級別(省、市、縣)', `parent_region_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '上級地理區域編碼', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; INSERT INTO `region` VALUES (1, '110000', '北京', 0, NULL); INSERT INTO `region` VALUES (2, '410000', '河南省', 0, NULL); INSERT INTO `region` VALUES (3, '110100', '北京市', 1, '110000'); INSERT INTO `region` VALUES (4, '410100', '鄭州市', 1, '410000'); DROP TABLE IF EXISTS `store_info`; CREATE TABLE `store_info` ( `id` bigint(20) NOT NULL COMMENT 'id', `store_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '店鋪名稱', `reputation` int(11) NULL DEFAULT NULL COMMENT '信譽等級', `region_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '店鋪所在地', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; INSERT INTO `store_info` VALUES (1, 'XX零食店', 4, '110100'); INSERT INTO `store_info` VALUES (2, 'XX飲品店', 3, '410100'); 

建立product_db_一、product_db_2數據庫,並分別對兩庫執行如下腳本建立表:spring

DROP TABLE IF EXISTS `product_descript_1`; CREATE TABLE `product_descript_1` ( `id` bigint(20) NOT NULL COMMENT 'id', `product_info_id` bigint(20) NULL DEFAULT NULL COMMENT '所屬商品id', `descript` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '商品描述',`store_info_id` bigint(20) NULL DEFAULT NULL COMMENT '所屬店鋪id', PRIMARY KEY (`id`) USING BTREE, INDEX `FK_Reference_2`(`product_info_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; DROP TABLE IF EXISTS `product_descript_2`; CREATE TABLE `product_descript_2` ( `id` bigint(20) NOT NULL COMMENT 'id', `product_info_id` bigint(20) NULL DEFAULT NULL COMMENT '所屬商品id', `descript` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '商品描述', `store_info_id` bigint(20) NULL DEFAULT NULL COMMENT '所屬店鋪id', PRIMARY KEY (`id`) USING BTREE, INDEX `FK_Reference_2`(`product_info_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; DROP TABLE IF EXISTS `product_info_1`; CREATE TABLE `product_info_1` ( `product_info_id` bigint(20) NOT NULL COMMENT 'id', `store_info_id` bigint(20) NULL DEFAULT NULL COMMENT '所屬店鋪id', `product_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '商品名稱', `spec` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '規 格', `region_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '產地', `price` decimal(10, 0) NULL DEFAULT NULL COMMENT '商品價格', `image_url` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '商品圖片', PRIMARY KEY (`product_info_id`) USING BTREE, INDEX `FK_Reference_1`(`store_info_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; DROP TABLE IF EXISTS `product_info_2`; CREATE TABLE `product_info_2` ( `product_info_id` bigint(20) NOT NULL COMMENT 'id', `store_info_id` bigint(20) NULL DEFAULT NULL COMMENT '所屬店鋪id', `product_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '商品名稱', `spec` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '規 格', `region_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '產地', `price` decimal(10, 0) NULL DEFAULT NULL COMMENT '商品價格', `image_url` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '商品圖片', PRIMARY KEY (`product_info_id`) USING BTREE, INDEX `FK_Reference_1`(`store_info_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; DROP TABLE IF EXISTS `region`; CREATE TABLE `region` ( `id` bigint(20) NOT NULL COMMENT 'id', `region_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地理區域編碼', `region_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地理區域名稱', `level` tinyint(1) NULL DEFAULT NULL COMMENT '地理區域級別(省、市、縣)', `parent_region_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '上級地理區域編碼', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; INSERT INTO `region` VALUES (1, '110000', '北京', 0, NULL); INSERT INTO `region` VALUES (2, '410000', '河南省', 0, NULL); INSERT INTO `region` VALUES (3, '110100', '北京市', 1, '110000'); INSERT INTO `region` VALUES (4, '410100', '鄭州市', 1, '410000'); 

9.5.實現步驟

9.5.1搭建maven工程

(1)搭建工程maven工程shopping,並作好Spring boot相關配置。
(2)引入maven依賴sql

<dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding‐jdbc‐spring‐boot‐starter</artifactId> <version>4.0.0‐RC1</version> </dependency> 

9.5.2 分片配置

既然是分庫分表,那麼就須要定義多個真實數據源,每個數據庫連接信息就是一個數據源定義,如 :數據庫

spring.shardingsphere.datasource.m0.type = com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.m0.driver‐class‐name = com.mysql.jdbc.Driver spring.shardingsphere.datasource.m0.url = jdbc:mysql://localhost:3306/store_db?useUnicode=true spring.shardingsphere.datasource.m0.username = root spring.shardingsphere.datasource.m0.password = root 

m0,就是這個真實數據源的名稱,而後須要告訴Sharding-JDBC,咋們有那些真實數據源,如 :express

spring.shardingsphere.datasource.names = m0,m1,m2,s0,s1,s2 

若是須要配置讀寫分離,還須要告訴Sharding-JDBC,這麼多真實數據源,那麼有幾個是一套讀寫分離?也就是定義主從邏輯數據源 :apache

spring.shardingsphere.sharding.master‐slave‐rules.ds0.master‐data‐source‐name=m0 spring.shardingsphere.sharding.master‐slave‐rules.ds0.slave‐data‐source‐names=s0 

若咱們已經對m0和s0作了mysql主從同步,那咱們須要告訴Sharding-JDBC,m0、s0爲一組主從同步數據源,其 中m0爲主,s0爲從,而且定義名稱爲ds0,這個ds0就是主從邏輯數據源。
最終配置以下,具體的分庫分表策略參考註釋內容:編程

# 真實數據源定義 m爲主庫 s爲從庫 spring.shardingsphere.datasource.names = m0,m1,m2,s0,s1,s2 spring.shardingsphere.datasource.m0.type = com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.m0.driver‐class‐name = com.mysql.jdbc.Driver spring.shardingsphere.datasource.m0.url = jdbc:mysql://localhost:3306/store_db?useUnicode=true spring.shardingsphere.datasource.m0.username = root spring.shardingsphere.datasource.m0.password = root spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.m1.driver‐class‐name = com.mysql.jdbc.Driver spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/product_db_1? useUnicode=true spring.shardingsphere.datasource.m1.username = root spring.shardingsphere.datasource.m1.password = root spring.shardingsphere.datasource.m2.type = com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.m2.driver‐class‐name = com.mysql.jdbc.Driver spring.shardingsphere.datasource.m2.url = jdbc:mysql://localhost:3306/product_db_2? useUnicode=true spring.shardingsphere.datasource.m2.username = root spring.shardingsphere.datasource.m2.password = root spring.shardingsphere.datasource.s0.type = com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.s0.driver‐class‐name = com.mysql.jdbc.Driver spring.shardingsphere.datasource.s0.url = jdbc:mysql://localhost:3307/store_db?useUnicode=true spring.shardingsphere.datasource.s0.username = root spring.shardingsphere.datasource.s0.password = root spring.shardingsphere.datasource.s1.type = com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.s1.driver‐class‐name = com.mysql.jdbc.Driver spring.shardingsphere.datasource.s1.url = jdbc:mysql://localhost:3307/product_db_1? useUnicode=true spring.shardingsphere.datasource.s1.username = root spring.shardingsphere.datasource.s1.password = root spring.shardingsphere.datasource.s2.type = com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.s2.driver‐class‐name = com.mysql.jdbc.Driver spring.shardingsphere.datasource.s2.url = jdbc:mysql://localhost:3307/product_db_2? useUnicode=true spring.shardingsphere.datasource.s2.username = root spring.shardingsphere.datasource.s2.password = root # 主庫從庫邏輯數據源定義 ds0爲store_db ds1爲product_db_1 ds2爲product_db_2 spring.shardingsphere.sharding.master‐slave‐rules.ds0.master‐data‐source‐name=m0 spring.shardingsphere.sharding.master‐slave‐rules.ds0.slave‐data‐source‐names=s0 spring.shardingsphere.sharding.master‐slave‐rules.ds1.master‐data‐source‐name=m1 spring.shardingsphere.sharding.master‐slave‐rules.ds1.slave‐data‐source‐names=s1 spring.shardingsphere.sharding.master‐slave‐rules.ds2.master‐data‐source‐name=m2 spring.shardingsphere.sharding.master‐slave‐rules.ds2.slave‐data‐source‐names=s2 # 默認分庫策略,以store_info_id爲分片鍵,分片策略爲store_info_id % 2 + 1,也就是store_info_id爲雙數的 數據進入ds1,爲單數的進入ds2 spring.shardingsphere.sharding.default‐database‐strategy.inline.sharding‐column = store_info_id spring.shardingsphere.sharding.default‐database‐strategy.inline.algorithm‐expression = ds$‐> {store_info_id % 2 + 1} # store_info分表策略,固定分配至ds0的store_info真實表 spring.shardingsphere.sharding.tables.store_info.actual‐data‐nodes = ds$‐>{0}.store_info spring.shardingsphere.sharding.tables.store_info.table‐strategy.inline.sharding‐column = id spring.shardingsphere.sharding.tables.store_info.table‐strategy.inline.algorithm‐expression = store_info # product_info分表策略,分佈在ds1,ds2的product_info_1 product_info_2表 ,分片策略爲product_info_id % 2 + 1,product_info_id生成爲雪花算法,爲雙數的數據進入product_info_1表,爲單數的進入product_info_2 表 spring.shardingsphere.sharding.tables.product_info.actual‐data‐nodes = ds$‐> {1..2}.product_info_$‐>{1..2} spring.shardingsphere.sharding.tables.product_info.table‐strategy.inline.sharding‐column = product_info_id spring.shardingsphere.sharding.tables.product_info.table‐strategy.inline.algorithm‐expression = product_info_$‐>{product_info_id % 2 + 1} spring.shardingsphere.sharding.tables.product_info.key‐generator.column=product_info_id spring.shardingsphere.sharding.tables.product_info.key‐generator.type=SNOWFLAKE # product_descript分表策略,分佈在ds1,ds2的product_descript_1 product_descript_2表 ,分片策略爲 product_info_id % 2 + 1,id生成爲雪花算法,product_info_id爲雙數的數據進入product_descript_1表,爲單 數的進入product_descript_2 spring.shardingsphere.sharding.tables.product_descript.actual‐data‐nodes = ds$‐> {1..2}.product_descript_$‐>{1..2} spring.shardingsphere.sharding.tables.product_descript.table‐strategy.inline.sharding‐column = product_info_id spring.shardingsphere.sharding.tables.product_descript.table‐strategy.inline.algorithm‐ expression = product_descript_$‐>{product_info_id % 2 + 1} spring.shardingsphere.sharding.tables.product_descript.key‐generator.column=id spring.shardingsphere.sharding.tables.product_descript.key‐generator.type=SNOWFLAKE # 設置product_info,product_descript爲綁定表 spring.shardingsphere.sharding.binding‐tables[0] = product_info,product_descript # 設置region爲廣播表(公共表),每次更新操做會發送至全部數據源 spring.shardingsphere.sharding.broadcast‐tables=region # 打開sql輸出日誌 spring.shardingsphere.props.sql.show = true 

9.5.3 添加商品

實體類 :
在這裏插入圖片描述
DAO實現

@Mapper @Component public interface ProductDao { //添加商品基本信息 @Insert("insert into product_info(store_info_id,product_name,spec,region_code,price) value(#{storeInfoId},#{productName},#{spec},#{regionCode},#{price})") @Options(useGeneratedKeys = true,keyProperty = "productInfoId",keyColumn = "id") int insertProductInfo(ProductInfo productInfo); //添加商品描述信息 @Insert("insert into product_descript(product_info_id,descript,store_info_id) value(# {productInfoId},#{descript},#{storeInfoId})") @Options(useGeneratedKeys = true,keyProperty = "id",keyColumn = "id") int insertProductDescript(ProductDescript productDescript); } 

service實現,針對垂直分庫的兩個庫,分別實現店鋪服務、商品服務

@Service public class ProductServiceImpl implements ProductService { @Autowired private ProductDao productDao; @Override @Transactional public void createProduct(ProductInfo product) { ProductDescript productDescript = new ProductDescript(); productDescript.setDescript(product.getDescript()); productDao.insertProductInfo(product);//新增商品基本信息 productDescript.setProductInfoId(product.getProductInfoId()); productDescript.setStoreInfoId(product.getStoreInfoId()); //冗餘店鋪信息 productDao.insertProductDescript(productDescript);//新增商品描述信息 } } 

controller實現:

/** * 賣家商品展現 */ @RestController public class SellerController { @Autowired private ProductService productService; @PostMapping("/products") public String createProject(@RequestBody ProductInfo productInfo) { productService.createProduct(productInfo); return "建立成功!"; } 

單元測試:

@RunWith(SpringRunner.class) @SpringBootTest(classes = ShardingJdbcDemoBootstrap.class) public class ShardingTest { @Autowired ProductService productService; @Test public void testCreateProduct(){ for(long i=1;i<10;i++){ //store_info_id,product_name,spec,region_code,price,image_url ProductInfo productInfo = new ProductInfo(); productInfo.setProductName("Java編程思想"+i); productInfo.setDescript("Java編程思想是一本很是好的Java教程"+i); productInfo.setRegionCode("110000"); productInfo.setStoreInfoId(1); productInfo.setPrice(new BigDecimal(i)); productService.createProduct(productInfo); } } 

這是使用了sharding-jdbc所提供的全局主鍵生成方式之一雪花算法,來生成全局業務惟一主鍵。經過添加商品接口新增商品進行分庫驗證,store_info_id爲偶數的數據在product_db_1,爲奇數的數據在product_db_2。
經過添加商品接口新增商品進行分表驗證,product_id爲偶數的數據在product_info_一、product_descript_1,爲奇數的數據在product_info_二、produt_descript_2。

9.5.4 查詢商品

Dao實現 :
在ProductDao中定義商品查詢方法 :

@Select("select i.*, d.descript, r.region_name placeOfOrigin " + "from product_info i join product_descript d on i.id = d.product_info_id " + "join region r on r.region_code = i.region_code order by i.id desc limit #{start},# {pageSize}") List<ProductInfo> selectProductList(@Param("start")int start,@Param("pageSize") int pageSize); 

Service實現 :
在ProductServiceImpl定義商品查詢方法 :

@Override public List<ProductInfo> queryProduct(int page,int pageSize) { int start = (page‐1)*pageSize; return productDao.selectProductList(start,pageSize); } 

Controller實現 :

@GetMapping(value = "/products/{page}/{pageSize}") public List<ProductInfo> queryProduct(@PathVariable("page")int page,@PathVariable("pageSize")int pageSize){ return productService.queryProduct(page,pageSize); } 

單元測試 :

@Test public void testSelectProductList(){ List<ProductInfo> productInfos = productService.queryProduct(1,10); System.out.println(productInfos); } 

經過查詢商品列表接口,可以查詢到全部分片的商品信息,關聯的地理區域,店鋪信息正確。
總結 :
分頁查詢是業務中最多見的場景,Sharding-jdbc支持經常使用關係數據庫的分頁查詢,不過Sharding-jdbc的分頁功能比較容易讓使用者誤解,用戶一般認爲分頁歸併會佔用大量內存。在分佈式的場景中,將LIMIT 10000000,10改寫爲Limit 0,10000000,才能保證其數據的正確性。用戶很是容易產生ShardingSphere會將大量無心義的數據加載至內存中,形成內存溢出風險的錯覺。其實大部分狀況都經過流式歸併獲取數據結果集,所以Sharding-Sphere會經過結果集的next方法將無需取出的數據所有跳過,並不會將其存入內存。
但同時須要注意的是,因爲排序的須要,大量的數據仍然須要傳輸到Sharding-JDBC的內存空間。所以,採用LIMIT這種方式分頁,並不是最佳實踐。因爲LIMIT並不能經過索引查詢數據,所以若是能夠保證ID的連續性,經過ID進行分頁是比較好的解決方案,例如 :

SELECT * FROM t_order WHERE id > 100000 AND id <= 100010 ORDER BY id; 

或經過記錄上次查詢結果的最後一條記錄的ID進行下一頁的查詢,例如 :

SELECT * FROM t_order WHERE id > 10000000 LIMIT 10; 

排序功能是由Sharding-jdbc的排序歸併來完成,因爲在SQL中存在ORDER BY語句,所以每一個數據結果集自身是有序的,所以只須要將數據結果集當前遊標指向的數據值進行排序便可。這至關於多個有序的數組進行排序,歸併排序是最適合此場景的排序算法。

9.5.5 統計商品

本小節實現商品總數統計,商品分組統計
Dao實現,在ProductDao中定義 :

//總數統計 @Select("select count(1) from product_info") int selectCount(); //分組統計 @Select("select count(1) as num from product_info group by region_code having num>1 ORDER BY region_code ASC") List<Map> selectProductGroupList(); 

單元測試 :

@Test public void testSelectCount(){ int i = productDao.selectCount(); System.out.println(i); } @Test public void testSelectGroupList(){ List<Map> maps = productDao.selectProductGroupList(); System.out.println(maps); } 

總結 :
分組統計
分組統計也是業務中常見的場景,分組功能的實現由Sharding-jdbc分組歸併完成。分組歸併的狀況最爲複雜,它分爲流式分組歸併和內存分組歸併。流式分組歸併要求SQL的排序項與分組項的字段必須保存一致,不然只能經過內存歸併才能保證其數據的正確性。
舉例說明,假設根據科目分片,表結構中包含考生的姓名(爲了簡單起見,不考慮重名的狀況)和分數。經過SQL獲取每位考生的總分,可經過以下SQL :

SELECT name, SUM(score) FROM t_score GROUP BY name ORDER BY name; 

在分組項與排序項徹底一致的狀況下,取得的數據是連續的,分組所需的數據全數存在於各個數據結果集的當前遊標所指向的數據值,所以能夠採用流式歸併。以下圖所示。
在這裏插入圖片描述
進行歸併時,邏輯與排序歸併相似。 下圖展示了進行next調用的時候,流式分組歸併是如何進行的。
在這裏插入圖片描述
經過圖中咱們能夠看到,當進行第一次next調用時,排在隊列首位的t_score_java將會被彈出隊列,而且將分組值 同爲「Jetty」的其餘結果集中的數據一同彈出隊列。 在獲取了全部的姓名爲「Jetty」的同窗的分數以後,進行累加操 做,那麼,在第一次next調用結束後,取出的結果集是「Jetty」的分數總和。 與此同時,全部的數據結果集中的遊標 都將下移至數據值「Jetty」的下一個不一樣的數據值,而且根據數據結果集當前遊標指向的值進行重排序。 所以,包含 名字順着第二位的「John」的相關數據結果集則排在的隊列的前列。

10. 總結

爲何分庫分表?分庫分表就是爲了解決因爲數據量過大而致使數據庫性能下降的問題,將原來獨立的數據庫拆分紅若干數據庫組成 ,將數據大表拆分紅若干數據表組成,使得單一數據庫、單一數據表的數據量變小,從而達到提高數據庫性能的目的。
分庫分表方式:垂直分表、垂直分庫、水平分庫、水平分表
分庫分錶帶來問題:因爲數據分散在多個數據庫,服務器致使了事務一致性問題、跨節點join問題、跨節點分頁、 排序、函數,主鍵須要全局惟一,公共表。
Sharding-JDBC基礎概念:邏輯表,真實表,數據節點,綁定表,廣播表,分片鍵,分片算法,分片策略,主鍵生 成策略
Sharding-JDBC核心功能:數據分片,讀寫分離
Sharding-JDBC執行流程: SQL解析 => 查詢優化 => SQL路由 => SQL改寫 => SQL執行 => 結果歸併
最佳實踐:
系統在設計之初就應該對業務數據的耦合鬆緊進行考量,從而進行垂直分庫、垂直分表,使數據層架構清晰明瞭。
若非必要,無需進行水平切分,應先從緩存技術着手下降對數據庫的訪問壓力。若是緩存使用事後,數據庫訪問量 仍是很是大,能夠考慮數據庫讀、寫分離原則。若當前數據庫壓力依然大,且業務數據持續增加沒法估量,最後可 考慮水平分庫、分表,單表拆分數據控制在1000萬之內。
附 SQL支持說明
詳細參考:https://shardingsphere.apache.org/document/current/cn/features/sharding/use-norms/sql/ 說明:如下爲官方顯示內容,具體是否適用以實際測試爲準 。
支持的SQL

SQL 必要條件
SELECT * FROM tbl_name  
SELECT * FROM tbl_name WHERE (col1 = ? or col2 = ?) and col3 = ?  
   
SELECT * FROM tbl_name WHERE col1 = ? ORDER BY col2 DESC LIMIT ?  
SELECT COUNT(*), SUM(col1), MIN(col1), MAX(col1), AVG(col1) FROM tbl_name WHERE col1 =?  
SELECT COUNT(col1) FROM tbl_name WHERE col2 = ? GROUP BY col1 ORDER BY col3 DESC LIMIT ?, ?  
INSERT INTO tbl_name (col1, col2,…) VALUES (?, ?, …)  
INSERT INTO tbl_name VALUES (?, ?,…)  
INSERT INTO tbl_name (col1, col2, …) VALUES (?, ?, …), (?, ?, …)  
UPDATE tbl_name SET col1 = ? WHERE col2 = ?  
DELETE FROM tbl_name WHERE col1 = ?  
CREATE TABLE tbl_name (col1 int, …)  
ALTER TABLE tbl_name ADD col1 varchar(10)  
DROP TABLE tbl_name  
TRUNCATE TABLE tbl_name  
CREATE INDEX idx_name ON tbl_name  
DROP INDEX idx_name ON tbl_name  
DROP INDEX idx_name  
SELECT DISTINCT * FROM tbl_name WHERE col1 = ?  
SELECT COUNT(DISTINCT col1) FROM tbl_name  

不支持的SQL

SQL 不支持緣由
INSERT INTO tbl_name (col1, col2, …) VALUES(1+2, ?, …) VALUES語句不支持運算 表達式
INSERT INTO tbl_name (col1, col2, …) SELECT col1, col2, … FROM tbl_name WHERE col3 = ? INSERT … SELECT
SELECT COUNT(col1) as count_alias FROM tbl_name GROUP BY col1 HAVING count_alias > ? HAVING
SELECT * FROM tbl_name1 UNION SELECT * FROM tbl_name2 UNION
SELECT * FROM tbl_name1 UNION ALL SELECT * FROM tbl_name2 UNION ALL
SELECT * FROM ds.tbl_name1 包含schema
SELECT SUM(DISTINCT col1), SUM(col1) FROM tbl_name 詳見DISTINCT支持狀況詳 細說明

DISTINCT支持狀況詳細說明
支持的SQL

SELECT DISTINCT * FROM tbl_name WHERE col1 = ? SELECT DISTINCT col1 FROM tbl_name SELECT DISTINCT col1, col2, col3 FROM tbl_name SELECT DISTINCT col1 FROM tbl_name ORDER BY col1 SELECT DISTINCT col1 FROM tbl_name ORDER BY col2 SELECT DISTINCT(col1) FROM tbl_name SELECT AVG(DISTINCT col1) FROM tbl_name SELECT SUM(DISTINCT col1) FROM tbl_name SELECT COUNT(DISTINCT col1) FROM tbl_name SELECT COUNT(DISTINCT col1) FROM tbl_name GROUP BY col1 SELECT COUNT(DISTINCT col1 + col2) FROM tbl_name SELECT COUNT(DISTINCT col1), SUM(DISTINCT col1) FROM tbl_name SELECT COUNT(DISTINCT col1), col1 FROM tbl_name GROUP BY col1 SELECT col1, COUNT(DISTINCT col1) FROM tbl_name GROUP BY col1 

不支持的SQL

SQL 不支持緣由
SELECT SUM(DISTINCT col1), SUM(col1) FROM tbl_name 同時使用普通聚合函數和DISTINCT聚合函數
相關文章
相關標籤/搜索