Springboot + Mybatis-Plus + Sharding-JDBC
現在開源的分庫分表方案中, 常用的就是MyCat和Sharding-JDBC, MyCat需要有專門的服務器, 通常要的配置也比較高, 公司之前就是專門部署了兩台64G的MyCat服務器, 后面為了節省資源, 逐步切換成Sharding-JDBC
這里主要介紹Springboot, Mybatis-Plus, Sharding-JDBC的整合, 基於Springboot的yml配置文件配置分庫分表策略, 使用snowflake雪花算法生成分布式主鍵, 分片策略是哈希取模
-
pom文件
<dependencies> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>1.5.6</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency> <dependency> <groupId>com.eric</groupId> <artifactId>common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.eric</groupId> <artifactId>cache</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!--dozer依賴開始--> <dependency> <groupId>net.sf.dozer</groupId> <artifactId>dozer</artifactId> <version>5.5.1</version> <exclusions> <!--目前項目中使用的是apache自帶的log4j--> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> <!--<exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion>--> </exclusions> </dependency> <dependency> <groupId>net.sf.dozer</groupId> <artifactId>dozer-spring</artifactId> <version>5.5.1</version> </dependency> <!--dozer依賴結束--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.41</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.6</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.32</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version> </dependency> <!-- mybatis的分頁插件 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.7</version> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> <version>1.18.2</version> </dependency> <!--sharding-jdbc依賴開始--> <!-- for spring boot --> <dependency> <groupId>io.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>${sharding-sphere.version}</version> </dependency> <!-- for spring namespace --> <dependency> <groupId>io.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-namespace</artifactId> <version>${sharding-sphere.version}</version> </dependency> <!--sharding-jdbc依賴結束--> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Camden.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <finalName>${project.artifactId}</finalName> <plugins> <!-- 資源文件拷貝插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF8</encoding> </configuration> </plugin> </plugins> </build>
-
yml配置文件 需要創建兩個數據庫實例ds0和ds1, 每個數據實例要創建ordermaster0和ordermaster1兩個表
server: port: 12890 undertow: io-threads: 16 worker-threads: 256 buffer-size: 1024 buffers-per-region: 1024 direct-buffers: true accesslog: dir: my-undertow enabled: true pattern: '%t %a "%r" %s (%D ms)' # sharding-jdbc分庫分表的配置 sharding: jdbc: datasource: ds0: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/ds0 username: root password: 123456 ds1: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/ds1 username: root password: 123456 names: ds0,ds1 config: sharding: tables: # 分表策略 order_master: table-strategy: inline: sharding-column: order_id algorithm-expression: order_master$->{order_id % 2} key-generator-column-name: order_id actual-data-nodes: ds$->{0..1}.order_master$->{0..1} default-database-strategy: # 分庫策略 inline: sharding-column: user_id algorithm-expression: ds$->{user_id % 2} mybatis-plus: datasource: dataSource mapper-locations: classpath:/mapper/*.xml #實體掃描,多個package用逗號或者分號分隔 typeAliasesPackage: com.eric.shardingjdbc.bean global-config: #主鍵類型 0:"數據庫ID自增", 1:"用戶輸入ID",2:"全局唯一ID (數字類型唯一ID)", 3:"全局唯一ID UUID"; id-type: 1 #字段策略 0:"忽略判斷",1:"非 NULL 判斷"),2:"非空判斷" field-strategy: 2 #駝峰下划線轉換 db-column-underline: true #刷新mapper 調試神器 refresh-mapper: true #數據庫大寫下划線轉換 #capital-mode: true #序列接口實現類配置 #key-generator: com.baomidou.springboot.xxx #邏輯刪除配置 logic-delete-value: 0 logic-not-delete-value: 1 #自定義填充策略接口實現 #meta-object-handler: com.baomidou.springboot.xxx #自定義SQL注入器 #sql-injector: com.baomidou.springboot.xxx configuration: map-underscore-to-camel-case: true cache-enabled: false snowflake: datacenter: 1 # 數據中心的id bizType: sharding_order_id_ # 業務類型
-
bean對象
@Data @Accessors(chain = true) public class OrderMaster implements Serializable { /** * 訂單ID */ @TableId private Long orderId; /** * 訂單編號 */ private String orderSn; /** * 下單人ID */ private Long userId; private static final long serialVersionUID = 1L; }
-
mapper(mapper.xml可以生成空的xml)
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.eric.shardingjdbc.bean.OrderMaster; /** * @author wang.js on 2019/1/24. * @version 1.0 */ public interface OrderMasterMapper extends BaseMapper<OrderMaster> { }
-
service層
/** * 主訂單 * * @author wang.js on 2019/3/12. * @version 1.0 */ public interface OrderMasterService { /** * 保存訂單 * * @param t 訂單 * @return boolean */ boolean insert(OrderMaster t); /** * 查詢訂單 * @param i * @return */ OrderMaster findById(int i); } /** * @author wang.js on 2019/3/12. * @version 1.0 */ @Service public class OrderMasterServiceImpl extends ServiceImpl<OrderMasterMapper, OrderMaster> implements OrderMasterService { @Override public boolean insert(OrderMaster t) { return this.baseMapper.insert(t) > 0; } @Override public OrderMaster findById(int i) { return this.baseMapper.selectById(i); } }
-
sql腳本(在ds0和ds1中分別執行)
DROP TABLE IF EXISTS `order_master0`; CREATE TABLE `order_master0` ( `order_id` varchar(40) DEFAULT NULL COMMENT '訂單ID', `order_sn` varchar(40) DEFAULT NULL COMMENT '訂單編號', `user_id` varchar(40) DEFAULT NULL COMMENT '下單人ID', `shipping_user` varchar(40) DEFAULT NULL COMMENT '收貨人姓名', `payment_method` varchar(1) DEFAULT NULL COMMENT '支付方式', `order_money` int(11) DEFAULT NULL COMMENT '訂單金額', `district_money` int(11) DEFAULT NULL COMMENT '優惠金額(不包含優惠券)', `shipping_money` int(11) DEFAULT NULL COMMENT '運費金額', `payment_money` int(11) DEFAULT NULL COMMENT '支付金額', `shipping_sn` varchar(40) DEFAULT NULL COMMENT '快遞單號', `create_time` datetime DEFAULT NULL COMMENT '下單時間', `shipping_time` datetime DEFAULT NULL COMMENT '發貨時間', `pay_time` datetime DEFAULT NULL COMMENT '支付時間', `receive_time` datetime DEFAULT NULL COMMENT '收貨時間', `order_status` varchar(1) DEFAULT NULL COMMENT '訂單狀態', `order_point` int(11) DEFAULT NULL COMMENT '訂單積分', `update_time` datetime DEFAULT NULL COMMENT '更新時間', `coupon_discount` int(11) DEFAULT NULL COMMENT '優惠券金額' ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for order_master1 -- ---------------------------- DROP TABLE IF EXISTS `order_master1`; CREATE TABLE `order_master1` ( `order_id` varchar(40) DEFAULT NULL COMMENT '訂單ID', `order_sn` varchar(40) DEFAULT NULL COMMENT '訂單編號', `user_id` varchar(40) DEFAULT NULL COMMENT '下單人ID', `shipping_user` varchar(40) DEFAULT NULL COMMENT '收貨人姓名', `payment_method` varchar(1) DEFAULT NULL COMMENT '支付方式', `order_money` int(11) DEFAULT NULL COMMENT '訂單金額', `district_money` int(11) DEFAULT NULL COMMENT '優惠金額(不包含優惠券)', `shipping_money` int(11) DEFAULT NULL COMMENT '運費金額', `payment_money` int(11) DEFAULT NULL COMMENT '支付金額', `shipping_sn` varchar(40) DEFAULT NULL COMMENT '快遞單號', `create_time` datetime DEFAULT NULL COMMENT '下單時間', `shipping_time` datetime DEFAULT NULL COMMENT '發貨時間', `pay_time` datetime DEFAULT NULL COMMENT '支付時間', `receive_time` datetime DEFAULT NULL COMMENT '收貨時間', `order_status` varchar(1) DEFAULT NULL COMMENT '訂單狀態', `order_point` int(11) DEFAULT NULL COMMENT '訂單積分', `update_time` datetime DEFAULT NULL COMMENT '更新時間', `coupon_discount` int(11) DEFAULT NULL COMMENT '優惠券金額' ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
測試類
@Test public void t2() { Random random = new Random(); for (long i = 1; i < 100L; i++) { OrderMaster t = new OrderMaster().setOrderId(defaultKeyGenerator.generateKey().longValue()).setUserId((long) (random.nextInt(100) + 1)); orderMasterService.insert(t); } }
ps: 如果並發度不高的情況下, defaultKeyGenerator.generateKey()生成的id很大幾率會是偶數
項目已經上傳到gitee和github
gitee: https://gitee.com/ericwo/second-kill
github: https://github.com/wangjisong1993/second-kill