狂創客圈 經典圖書 : 《Netty Zookeeper Redis 高併發實戰》 面試必備 + 面試必備 + 面試必備 【博客園總入口 】html
瘋狂創客圈 經典圖書 : 《SpringCloud、Nginx高併發核心編程》 大廠必備 + 大廠必備 + 大廠必備 【博客園總入口 】java
入大廠+漲工資必備: 高併發【 億級流量IM實戰】 實戰系列 【 SpringCloud Nginx秒殺】 實戰系列 【博客園總入口 】mysql
組件 | 連接地址 |
---|---|
windows centos 虛擬機 安裝&排坑 | vagrant+java+springcloud+redis+zookeeper鏡像下載(&製做詳解)) |
centos mysql 安裝&排坑 | centos mysql 筆記(內含vagrant mysql 鏡像) |
linux kafka安裝&排坑 | kafka springboot (或 springcloud ) 整合 |
Linux openresty 安裝 | Linux openresty 安裝 |
【必須】Linux Redis 安裝(帶視頻) | Linux Redis 安裝(帶視頻) |
【必須】Linux Zookeeper 安裝(帶視頻) | Linux Zookeeper 安裝, 帶視頻 |
Windows Redis 安裝(帶視頻) | Windows Redis 安裝(帶視頻) |
RabbitMQ 離線安裝(帶視頻) | RabbitMQ 離線安裝(帶視頻) |
ElasticSearch 安裝, 帶視頻 | ElasticSearch 安裝, 帶視頻 |
Nacos 安裝(帶視頻) | Nacos 安裝(帶視頻) |
【必須】Eureka | Eureka 入門,帶視頻 |
【必須】springcloud Config 入門,帶視頻 | springcloud Config 入門,帶視頻 |
【必須】SpringCloud 腳手架打包與啓動 | SpringCloud腳手架打包與啓動 |
Linux 自啓動 假死自啓動 定時自啓 | Linux 自啓動 假死啓動 |
組件 | 連接地址 |
---|---|
準備一: 在window安裝虛擬機集羣 | vagrant+java+springcloud+redis+zookeeper鏡像下載(&製做詳解)) |
準備二:在虛擬機上安裝 mysql ,至少須要兩個mysql節點 | centos mysql 筆記(內含vagrant mysql 鏡像) |
Sharding-JDBC 從入門到精通之一 | 入門實戰 |
Sharding-JDBC 從入門到精通之二 | 基本原理 |
Sharding-JDBC 從入門到精通之源碼 | git |
有關Sharding-JDBC介紹這裏就不在多說,以前Sharding-JDBC是噹噹網自研的關係型數據庫的水平擴展框架,如今已經捐獻給Apache,其原理請參見後面的博客。linux
shardingsphere文檔地址是:https://shardingsphere.apache.org/document/current/cn/overview/。nginx
在深刻了解以前,先實戰一把,增長印象, 激發興趣。git
通常狀況下,你們都會使用水平切分庫和表:將一張表水平切分紅多張表,還能夠放到多個庫中。這就涉及到數據分片的規則,比較常見的有:Hash取模分表、數值Range分表、一致性Hash算法分表。面試
概念 通常採用Hash取模的切分方式,例如:假設按goods_id分4張表。(goods_id%4 取整肯定表)redis
後期分片集羣擴容時,須要遷移舊的數據很難。算法
容易面臨跨分片查詢的複雜問題。好比上例中,若是頻繁用到的查詢條件中不帶goods_id時,將會致使沒法定位數據庫,從而須要同時向4個庫發起查詢,
再在內存中合併數據,取最小集返回給應用,分庫反而成爲拖累。spring
概念 按照時間區間或ID區間來切分。例如:將goods_id爲11000的記錄分到第一個表,10012000的分到第二個表,以此類推。
一致性Hash算法能很好的解決由於Hash取模而產生的分片集羣擴容時,須要遷移舊的數據的難題。至於具體原理這裏就不詳細說,
能夠參考一篇博客:一致性哈希算法(分庫分表,負載均衡等)
假設一個訂單表的user_id和order_id 分佈較爲均勻,按照1000W的數據規模,可使用以下的分庫、分表結構來保存:
db0 ├── t_order0 └── t_order1 db1 ├── t_order0 └── t_order1
簡單的進行分庫分表: 按照user_id %2 的規則進行分庫,按照 order_id %2 的規則進行分表
邏輯訂單表的結構以下:
DROP TABLE IF EXISTS `t_order_0`; DROP TABLE IF EXISTS `t_order_1`; DROP TABLE IF EXISTS `t_config`; CREATE TABLE `t_order_0` (`order_id` bigInt NOT NULL, `user_id` INT NOT NULL, `status` VARCHAR(45) NULL, PRIMARY KEY (`order_id`)); CREATE TABLE `t_order_1` (`order_id` bigInt NOT NULL, `user_id` INT NOT NULL, `status` VARCHAR(45) NULL, PRIMARY KEY (`order_id`));
DROP TABLE IF EXISTS `t_order_0`; DROP TABLE IF EXISTS `t_order_1`; DROP TABLE IF EXISTS `t_config`; CREATE TABLE `t_order_0` (`order_id` bigInt NOT NULL, `user_id` INT NOT NULL, `status` VARCHAR(45) NULL, PRIMARY KEY (`order_id`)); CREATE TABLE `t_order_1` (`order_id` bigInt NOT NULL, `user_id` INT NOT NULL, `status` VARCHAR(45) NULL, PRIMARY KEY (`order_id`));
兩個db上,都有t_order_0,和t_order_1兩個表
本文分庫樣例比較簡單,根據數據庫表中字段user_id%2進行判斷,若是user_id%2==0則使用ds0,不然使用ds1。
分樣例比較簡單,根據數據庫表中字段order_id%2進行判斷,若是order_id%2==0則使用t_order_0,不然使用t_order_1。
對 t_order 表進行的以下圖所示的數據表水平 分庫和分表,具體以下圖所示:
(對 t_order_item 表也要進行相似的水平分片,可是這部分配置省略了):
在 yml 配置文件中,可使用 Groovy 表達式,進行分庫分表的規則配置,具體的 Groovy 表達式以下:
表達式一: 例如 ds0.t_order_0 ds$->{0..1}.t_order_$->{0..1} 表達式一:db 維度的拆分, 例如 ds_0、ds_1 ds_${user_id % 2} 表達式一:table 維度的拆分, 例如 t_order_1 t_order_${order_id % 2}
這些表達式被稱爲 Groovy 表達式,它們的含義很容易識別:
1)對 t_order 進行兩種維度的拆分:db 維度和 table 維度;
2)在db 維度,user_id % 2 == 0 的記錄所有落到 ds0,user_id % 2 == 1 的記錄所有落到 ds1;(有人稱這一過程爲水平分庫,其實它的本質仍是在水平地分表,只不過依據表中 user_id 的不一樣把拆分的後的表放入兩個數據庫實例。)
3)在表維度,order_id% 2 == 0 的記錄所有落到 t_order0,order_id% 2 == 1 的記錄所有落到 t_order1。
4)對記錄的讀和寫都按照這種方向進行,「方向」,就是分片方式,就是路由。
使用這種簡潔的 Groovy 表達式, 能夠設置的分片策略和分片算法。可是這種方式所能表達的含義是有限的。所以,官方提供了分片策略接口和分片算法接口,讓大家利用 Java 代碼盡情表達更爲複雜的分片策略和分片算法。
實際上,分片算法是分片策略的組成部分,分片策略設置=分片鍵設置+分片算法設置。上述配置裏使用的策略是 Inline 類型的分片策略,使用的算法是 Inline 類型的行表達式算法。
具體的配置以下:
spring: application: name: sharding-jdbc-provider jpa: #配置自動建表:updata:沒有表新建,有表更新操做,控制檯顯示建表語句 hibernate: ddl-auto: none dialect: org.hibernate.dialect.MySQL5InnoDBDialect show-sql: true freemarker: allow-request-override: false allow-session-override: false cache: false charset: UTF-8 check-template-location: true content-type: text/html enabled: true expose-request-attributes: false expose-session-attributes: false expose-spring-macro-helpers: true prefer-file-system-access: true settings: classic_compatible: true default_encoding: UTF-8 template_update_delay: 0 suffix: .ftl template-loader-path: classpath:/templates/ shardingsphere: props: sql: show: true # 配置真實數據源 datasource: common: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver validationQuery: SELECT 1 FROM DUAL names: ds0,ds1 ds0: url: jdbc:mysql://cdh1:3306/store?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=true&serverTimezone=UTC username: root password: 123456 # 配置第 2 個數據源 org.apache.commons.dbcp2 ds1: url: jdbc:mysql://cdh2:3306/store?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=true&serverTimezone=UTC username: root password: 123456 # 配置分片規則和分片算法 rules: # 配置分片規則 sharding: tables: # 配置 t_order 表規則 t_order: actualDataNodes: ds$->{0..1}.t_order_$->{0..1} # 配置分庫策略 databaseStrategy: standard: shardingColumn: user_id shardingAlgorithmName: database-inline # 配置分表策略 tableStrategy: standard: shardingColumn: order_id shardingAlgorithmName: table-inline keyGenerateStrategy: column: order_id keyGeneratorName: snowflake # 配置分片算法 bindingTables: t_order sharding-algorithms: database-inline: type: INLINE props: algorithm-expression: ds$->{user_id % 2} table-inline: type: INLINE props: algorithm-expression: t_order_$->{order_id % 2} keyGenerators: snowflake: type: SNOWFLAKE props: workerId: 123
本文使用SpringBoot2,SpringData-JPA,Druid鏈接池,和噹噹的sharding-jdbc 5。
新建項目,加入噹噹的sharding-jdbc-core依賴和druid鏈接池。請參見源碼工程。
使用@EnableTransactionManagement開啓事務,
使用@EnableConfigurationProperties註解加入配置實體,啓動類完整代碼請入所示。
package com.crazymaker.springcloud.sharding.jdbc.demo.start; @EnableConfigurationProperties @SpringBootApplication(scanBasePackages = {"com.crazymaker.springcloud.sharding.jdbc.demo", // "com.crazymaker.springcloud.base", // "com.crazymaker.springcloud.standard" }, exclude = { DataSourceAutoConfiguration.class, SecurityAutoConfiguration.class, DruidDataSourceAutoConfigure.class}) @EnableScheduling @EnableSwagger2 @EnableJpaRepositories(basePackages = { "com.crazymaker.springcloud.sharding.jdbc.demo.dao.impl", // "com.crazymaker.springcloud.base.dao" }) @EnableTransactionManagement(proxyTargetClass = true) @EntityScan(basePackages = { // "com.crazymaker.springcloud.user.*.dao.po", "com.crazymaker.springcloud.sharding.jdbc.demo.entity.jpa", // "com.crazymaker.springcloud.standard.*.dao.po" }) /** * 啓用 Hystrix */ @EnableHystrix @EnableFeignClients( basePackages = "com.crazymaker.springcloud.user.info.remote.client", defaultConfiguration = FeignConfiguration.class) @Slf4j @EnableEurekaClient public class ShardingJdbcDemoCloudApplication { public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(ShardingJdbcDemoCloudApplication.class, args); Environment env = applicationContext.getEnvironment(); String port = env.getProperty("server.port"); String path = env.getProperty("server.servlet.context-path"); System.out.println("\n----------------------------------------------------------\n\t" + "Application is running! Access URLs:\n\t" + "Local: \t\thttp://localhost:" + port + path + "/index.html\n\t" + "swagger-ui: \thttp://localhost:" + port + path + "/swagger-ui.html\n\t" + "----------------------------------------------------------"); } }
就是簡單的實體和Repository,更多詳細內容請參見源碼工程。
/* * Copyright 2016-2018 shardingsphere.io. * <p> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * </p> */ package com.crazymaker.springcloud.sharding.jdbc.demo.entity.jpa; import com.crazymaker.springcloud.sharding.jdbc.demo.entity.Order; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "t_order") public final class OrderEntity extends Order { @Id @Column(name = "order_id") @GeneratedValue(strategy = GenerationType.IDENTITY) @Override public long getOrderId() { return super.getOrderId(); } @Column(name = "user_id") @Override public int getUserId() { return super.getUserId(); } @Column(name = "status") public String getStatus() { return super.getStatus(); } }
更多詳細內容請參見源碼工程。
接下來建立一個Controller進行測試,保存方法使用了插入數據和查看數據,根據咱們的規則,會每一個庫插入數據,同時我這裏還建立了一個查詢方法,查詢所有訂單。
package com.crazymaker.springcloud.sharding.jdbc.demo.controller; @RestController @RequestMapping("/api/sharding/") @Api(tags = "sharding jdbc 演示") public class ShardingJdbcController { @Resource JpaEntityService jpaEntityService; @PostMapping("/order/add/v1") @ApiOperation(value = "插入訂單") public RestOut<Order> orderAdd(@RequestBody Order dto) { jpaEntityService.addOrder(dto); return RestOut.success(dto); } @PostMapping("/order/list/v1") @ApiOperation(value = "查詢訂單") public RestOut<List<Order>> listAll() { List<Order> list = jpaEntityService.selectAll(); return RestOut.success(list); } }
啓動應用。
而後,在瀏覽器或HTTP請求工具訪問http://localhost:7700/sharding-jdbc-provider/swagger-ui.html,如圖所示
使用插入訂單的接口,能夠插入訂單, 注意 userid %2 ==0 進入 db1, 注意 userid %2 ==1進入 db2, 具體在哪一個表呢?
由於 orderid是經過雪花算法生成的,若是orderid%2==0 ,則進入t_order_0,不然使用t_order_1。
插入以後,能夠經過數據庫,看結果。具體以下圖:
使用程序的查詢所有的方法,shardingjdbc ,會查出全部的訂單。
使用shardingjdbc ,除了數據源的配置有些特殊的規則外, 持久層程序和普通的 JPA代碼,區別並不大。
固然,若是要實現特殊的分庫分表邏輯,仍是須要動代碼的,請看後續分解。
瘋狂創客圈 - Java高併發研習社羣,爲你們開啓大廠之門