springmvc+mybatis+shardingsphere(shardingjdbc)實現數據庫(mysql)讀寫分離架構

在存在大量讀操作的場景,可以採用數據庫讀寫分離的機制來加快查詢速度。

mysql本身就支持多服務實現讀寫分離,而springmvc要實現可以自己寫讀寫分離的代碼實現,其基本原理就是採用aop原理,攔截特定的自定義註解方法,通過不同的參數調用不同的數據源,這個網上有很多例子。

如果是已經存在的系統改造或者自己不想寫代碼,也可以採用已經成熟的框架,本人使用的是sharding-sphere的sharding-jdbc,官網是:http://shardingjdbc.io/

首先,要在springmvc項目的pom文件中添加包,代碼如下:

<!-- shardingjdbc -->
	<dependency>
	    <groupId>io.shardingsphere</groupId>
	    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
	    <version>3.0.0.M1</version>
	</dependency>
	
	<dependency>
	    <groupId>io.shardingsphere</groupId>
	    <artifactId>sharding-jdbc-spring-namespace</artifactId>
	    <version>3.0.0.M1</version>
	</dependency>

然後,準備好三個數據庫,一個主庫兩個從庫,新增回加入主庫,查詢回隨機走從庫,如果mysql配置了讀寫分離機制,會自動把主庫的數據同步到從庫。創建腳本如下:

-- 主庫
CREATE DATABASE `master`;

CREATE TABLE `t_order` (
  `order_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `business_id` int(4) DEFAULT NULL,
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `t_order` VALUES (1,1,112);

-- 從庫1
CREATE DATABASE `slave_1` ; 
CREATE TABLE `t_order` (
  `order_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `business_id` int(4) DEFAULT NULL,
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;

INSERT INTO `t_order` VALUES (2,2,112);

-- 從庫2
CREATE DATABASE `slave_2` ; 
CREATE TABLE `t_order` (
  `order_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `business_id` int(4) DEFAULT NULL,
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `t_order` VALUES (3,3,112);

數據庫信息配置文件db.properties配置如下:

#master
jdbc.master.driverClassName=com.mysql.jdbc.Driver
jdbc.master.url=jdbc:mysql://localhost:3306/master?useUnicode=true&characterEncoding=utf8
jdbc.master.username=root
jdbc.master.password=123456

#slave1
jdbc.slave1.driverClassName=com.mysql.jdbc.Driver
jdbc.slave1.url=jdbc:mysql://localhost:3306/slave_1?useUnicode=true&characterEncoding=utf8
jdbc.slave1.username=root
jdbc.slave1.password=123456

#slave2
jdbc.slave2.driverClassName=com.mysql.jdbc.Driver
jdbc.slave2.url=jdbc:mysql://localhost:3306/slave_2?useUnicode=true&characterEncoding=utf8
jdbc.slave2.username=root
jdbc.slave2.password=123456

spring配置文件,配置數據源代碼如下:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mongo="http://www.springframework.org/schema/data/mongo"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xmlns:master-slave="http://shardingsphere.io/schema/shardingsphere/masterslave"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/data/mongo
	   http://www.springframework.org/schema/data/mongo/spring-mongo-1.5.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit-1.6.xsd
       http://shardingsphere.io/schema/shardingsphere/masterslave  
       http://shardingsphere.io/schema/shardingsphere/masterslave/master-slave.xsd">
    <!--
     隱式地向 Spring 容器註冊AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor
    -->
    <context:annotation-config/>

    <context:component-scan base-package="com.shan">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.RestController"/>
    </context:component-scan>

    <!-- 配置 -->
    <bean id="property" class="com.shan.framework.property.MutilPropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath*:property/*.properties</value>
            </list>
        </property>
    </bean>

    <!-- db -->
    <bean id="master" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.master.driverClassName}" />
        <property name="url" value="${jdbc.master.url}" />
        <property name="username" value="${jdbc.master.username}" />
        <property name="password" value="${jdbc.master.password}" />
        
        <property name="maxActive" value="100"/>
	    <property name="initialSize" value="10"/>
	    <property name="maxWait" value="60000"/>
	    <property name="minIdle" value="5"/>
        
        <property name="testOnBorrow">
            <value>true</value>
        </property>
        <property name="validationQuery">
            <value>SELECT 1 FROM DUAL</value>
        </property>
    </bean>
    
    <bean id="slave1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.slave1.driverClassName}" />
        <property name="url" value="${jdbc.slave1.url}" />
        <property name="username" value="${jdbc.slave1.username}" />
        <property name="password" value="${jdbc.slave1.password}" />
        
        <property name="maxActive" value="100"/>
	    <property name="initialSize" value="10"/>
	    <property name="maxWait" value="60000"/>
	    <property name="minIdle" value="5"/>
        
        <property name="testOnBorrow">
            <value>true</value>
        </property>
        <property name="validationQuery">
            <value>SELECT 1 FROM DUAL</value>
        </property>
    </bean>
    
    <bean id="slave2" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.slave2.driverClassName}" />
        <property name="url" value="${jdbc.slave2.url}" />
        <property name="username" value="${jdbc.slave2.username}" />
        <property name="password" value="${jdbc.slave2.password}" />
        
        <property name="maxActive" value="100"/>
	    <property name="initialSize" value="10"/>
	    <property name="maxWait" value="60000"/>
	    <property name="minIdle" value="5"/>
        
        <property name="testOnBorrow">
            <value>true</value>
        </property>
        <property name="validationQuery">
            <value>SELECT 1 FROM DUAL</value>
        </property>
    </bean>
    
    <bean id="randomStrategy" class="io.shardingsphere.core.api.algorithm.masterslave.RandomMasterSlaveLoadBalanceAlgorithm" />

	<master-slave:data-source id="shardingDataSource" master-data-source-name="master" slave-data-source-names="slave1,slave2" strategy-ref="randomStrategy" />
	
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="shardingDataSource" />
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="shardingDataSource" />
        <property name="configLocation" value="classpath:/mybatis/mybatis-config.xml" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:mybatis/mapper/**/*Mapper.xml</value>
            </list>
        </property>
    </bean>

    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory" />
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />

</beans>

model文件UserModel.java代碼如下:

package com.shan.ssm.model;

public class UserModel {
	
	private Integer orderId;
	
	private Integer userId;
	
	private Integer businessId;

	public Integer getOrderId() {
		return orderId;
	}

	public void setOrderId(Integer orderId) {
		this.orderId = orderId;
	}

	public Integer getUserId() {
		return userId;
	}

	public void setUserId(Integer userId) {
		this.userId = userId;
	}

	public Integer getBusinessId() {
		return businessId;
	}

	public void setBusinessId(Integer businessId) {
		this.businessId = businessId;
	}

}

ShardingJdbcController.java代碼如下:

package com.shan.ssm.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.shan.ssm.service.IShardingJdbcService;

@Controller
@RequestMapping("sjc")
public class ShardingJdbcController {
	
	@Autowired
	private IShardingJdbcService iShardingJdbcService;
	
	@RequestMapping("/insert")
	@ResponseBody
	public String insert() {
		String msg = iShardingJdbcService.insert();
		return msg;
	}
	
	@RequestMapping("/list")
	@ResponseBody
	public String list() {
		String msg = iShardingJdbcService.list();
		return msg;
	}
	
}

IShardingJdbcService.java接口代碼如下:

package com.shan.ssm.service;

public interface IShardingJdbcService {
	
	public String insert();
	
	public String list();
	
}

ShardingJdbcService.java實現代碼如下:

package com.shan.ssm.service.impl;

import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang3.RandomUtils;
import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.shan.ssm.model.UserModel;
import com.shan.ssm.service.IShardingJdbcService;

@Service
public class ShardingJdbcService implements IShardingJdbcService {
	
	@Resource
	private SqlSession sqlSession;
	
	@Override
	public String insert() {
		UserModel um = new UserModel();
		um.setOrderId(RandomUtils.nextInt(10, 10000));
		um.setUserId(RandomUtils.nextInt(10, 10000));
		um.setBusinessId(RandomUtils.nextInt(10, 10000));
		int num = sqlSession.insert(ShardingJdbcService.class.getName() + ".insert", um);
		JSONObject obj = new JSONObject();
		if (num >= 0) {
			obj.put("code", 0);
			obj.put("msg", "成功");
		} else {
			obj.put("code", 1);
			obj.put("msg", "失敗");
		}
		return obj.toJSONString();
	}
	
	@Override
	public String list() {
		List<Map<String, Object>> list= sqlSession.selectList(ShardingJdbcService.class.getName() + ".list");
		JSONObject obj = new JSONObject();
		obj.put("list", list);
		return obj.toJSONString();
	}
	
}

mybatis的mapper文件ShardingJdbcServiceMapper.xml代碼如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.shan.ssm.service.impl.ShardingJdbcService">
	
	<insert id="insert" parameterType="com.shan.ssm.model.UserModel">
		INSERT INTO t_order
		(order_id,
		user_id,
		business_id)
		VALUES
		(#{orderId},
		#{userId},
		#{businessId}
		)
    </insert>
	
	<select id="list" resultType="java.util.HashMap">
		SELECT * FROM t_order 
	</select>
	
</mapper>

啓動程序後,測試插入,發現只會往主庫中插入數據,如圖:

查詢也是隨機調用的兩個從庫,如圖:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章