[关闭]
@boothsun 2017-11-19T13:06:39.000000Z 字数 4789 阅读 1950

sharding-JDBC 实现读写分离

中间件


需求

  1. 一主两从,做读写分离。
  2. 多个从库之间实现负载均衡。
  3. 可手动强制部分读请求到主库上。(因为主从同步有延迟,对实时性要求高的系统,可以将部分读请求也走主库)

本次不讨论 MySQL如何配置主从同步相关问题

库表SQL

  1. -- 主库
  2. CREATE DATABASE `master`;
  3. CREATE TABLE `t_order` (
  4. `order_id` int(11) NOT NULL,
  5. `user_id` int(11) NOT NULL,
  6. `business_id` int(4) DEFAULT NULL,
  7. PRIMARY KEY (`order_id`)
  8. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  9. INSERT INTO `t_order` VALUES (1,1,112);
  10. -- 从库1
  11. CREATE DATABASE `slave1` ;
  12. CREATE TABLE `t_order` (
  13. `order_id` int(11) NOT NULL,
  14. `user_id` int(11) NOT NULL,
  15. `business_id` int(4) DEFAULT NULL,
  16. PRIMARY KEY (`order_id`)
  17. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
  18. INSERT INTO `t_order` VALUES (2,2,112);
  19. -- 从库2
  20. CREATE DATABASE `slave2` ;
  21. CREATE TABLE `t_order` (
  22. `order_id` int(11) NOT NULL,
  23. `user_id` int(11) NOT NULL,
  24. `business_id` int(4) DEFAULT NULL,
  25. PRIMARY KEY (`order_id`)
  26. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  27. INSERT INTO `t_order` VALUES (3,3,112);

pom.xml

  1. <!-- https://mvnrepository.com/artifact/io.shardingjdbc/sharding-jdbc-core -->
  2. <dependency>
  3. <groupId>io.shardingjdbc</groupId>
  4. <artifactId>sharding-jdbc-core</artifactId>
  5. <version>2.0.0.M2</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>io.shardingjdbc</groupId>
  9. <artifactId>sharding-jdbc-spring-namespace</artifactId>
  10. <version>2.0.0.M2</version>
  11. </dependency>

spring配置文件

  1. <bean id="master" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
  2. destroy-method="close">
  3. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  4. <property name="url" value="${jdbc.url.master}"></property>
  5. <property name="username" value="${jdbc.username.master}"></property>
  6. <property name="password" value="${jdbc.password.master}"></property>
  7. <property name="maxActive" value="100"/>
  8. <property name="initialSize" value="10"/>
  9. <property name="maxWait" value="60000"/>
  10. <property name="minIdle" value="5"/>
  11. </bean>
  12. <bean id="slave1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
  13. destroy-method="close">
  14. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  15. <property name="url" value="${jdbc.url.slave1}"></property>
  16. <property name="username" value="${jdbc.username.slave1}"></property>
  17. <property name="password" value="${jdbc.password.slave1}"></property>
  18. <property name="maxActive" value="100"/>
  19. <property name="initialSize" value="10"/>
  20. <property name="maxWait" value="60000"/>
  21. <property name="minIdle" value="5"/>
  22. </bean>
  23. <bean id="slave2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
  24. destroy-method="close">
  25. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  26. <property name="url" value="${jdbc.url.slave2}"></property>
  27. <property name="username" value="${jdbc.username.slave2}"></property>
  28. <property name="password" value="${jdbc.password.slave2}"></property>
  29. <property name="maxActive" value="100"/>
  30. <property name="initialSize" value="10"/>
  31. <property name="maxWait" value="60000"/>
  32. <property name="minIdle" value="5"/>
  33. </bean>
  34. <bean id="randomStrategy" class="io.shardingjdbc.core.api.algorithm.masterslave.RandomMasterSlaveLoadBalanceAlgorithm" />
  35. <master-slave:data-source id="shardingDataSource" master-data-source-name="master" slave-data-source-names="slave1,slave2" strategy-ref="randomStrategy" />

单测

写:

  1. @Test
  2. public void insert() throws Exception {
  3. Order record = new Order();
  4. record.setBusinessId(112);
  5. record.setUserId(111);
  6. record.setOrderId(12212121);
  7. int result = orderMapper.insertSelective(record) ;
  8. System.out.println( result > 0 ? "插入成功" : "插入失败");
  9. }

运行结果:

查:
slave1 只有1条数据,主键order_id = 2 ; slave2 也只有1条数据,主键order_id = 3 。所以,如果查询到的结果orderId等于1就说明读请求进入到slave1,同理,如果查询出来的orderId等于0 就说明读请求进入到slave2。

  1. public void selectByExample3() throws Exception {
  2. final int[] slave1 = {0};
  3. final int[] slave2 = {0};
  4. for (int i = 0; i < 100; i++) {
  5. ((Runnable) () -> {
  6. OrderExample example = new OrderExample();
  7. example.createCriteria().andBusinessIdEqualTo(112);
  8. List<Order> orderList = orderMapper.selectByExample(example);
  9. if (orderList.get(0).getOrderId() == 2) {
  10. System.out.printf("读到slave1 读到的数据是{}", JSONObject.toJSONString(orderList.get(0)));
  11. slave1[0]++;
  12. } else if (orderList.get(0).getOrderId() == 3) {
  13. System.out.printf("读到slave2 读到的数据是{}", JSONObject.toJSONString(orderList.get(0)));
  14. slave2[0]++;
  15. }
  16. System.out.println(JSONObject.toJSONString(orderList));
  17. }).run();
  18. }
  19. System.out.println("+++++++++++++++++++++++++++++++++++++++");
  20. System.out.println("+++++++++++++++++++++++++++++++++++++++");
  21. System.out.println("slave1读到的次数-->" + slave1[0]);
  22. System.out.println("slave2读到的次数-->" + slave2[0]);
  23. System.out.println("+++++++++++++++++++++++++++++++++++++++");
  24. System.out.println("+++++++++++++++++++++++++++++++++++++++");
  25. }

运行截图:

强制路由

通常做读写分离,都会遇到的一个问题就是主从同步延迟。有时,为了简单解决主从同步问题,我们会想强制部分读请求到主库上,而非从库上。

HintManager 分片键值管理器

官方文档的解释:
基于暗示(Hint)的分片键值管理器

但是对于读写分离这种形式的强制路由 , 其实官方文档说的几个方法都不适用. 我们可使用hintManager.setMasterRouteOnly() .

单测

  1. @Test
  2. public void HintManagerTest() {
  3. HintManager hintManager = HintManager.getInstance() ;
  4. hintManager.setMasterRouteOnly();
  5. OrderExample example = new OrderExample();
  6. example.createCriteria().andBusinessIdEqualTo(112);
  7. List<Order> orderList = orderMapper.selectByExample(example);
  8. System.out.println(JSONObject.toJSONString(orderList));
  9. hintManager.close();
  10. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注