1. Preface
The read and write separation strategy of databases in a distributed environment is a key solution to solve the bottleneck of database read and write performance, and it also maximizes the speed and concurrency of reading (Read) data in applications.
When separating database read and write, we first need to configure the database master and slave. The simplest one is a master and a Slave (for large website systems, of course, it will be very complicated. Here we just analyze the simplest situation). Through the master-slave configuration, the master-slave database maintains the same data. We access the slave database Slave when performing read operations and the master database Master when performing write operations. This will reduce the pressure on a server.
When conducting case analysis of read and write separation. First, configure the master-slave replication of the database, and provide detailed explanation of the synchronous installation and configuration of the MySQL5.6 database master-slave (Master/Slave)
Of course, it is just a simple way to see how to use code to achieve the separation of the database read and write, and there is no need to configure the master and slave database. It only requires two machines with the same database installed.
2. Two ways to achieve reading and writing separation
Specifically in development, there are two common ways to achieve reading and writing separation:
1. The first method is the most commonly used method, which is to define two database connections, one is MasterDataSource and the other is SlaveDataSource. When updating the data, we read the MasterDataSource, and when querying the data, we read the SlaveDataSource. This method is very simple, so I won't go into details.
2. The second method of dynamic data source switching is to dynamically weave the data source into the program when the program is running, so as to choose to read the master library or the slave library. The main technologies used are: Annotation, spring AOP, reflection.
The implementation method will be described in detail below.
3. Aop realizes the read and write separation case of master-slave database
1. Project code address
The current project address of this demo: demo
2. Project structure
In the above figure, in addition to the marked code, the others are mainly configuration code and business code.
3. Specific analysis
This project is a demo of the SSM framework, Spring, Spring MVC and MyBatis. The specific configuration files are not introduced too much.
(1) UserContoller simulates reading and writing data
/** * Created by xuliugen on 2016/5/4. */@Controller@RequestMapping(value = "/user", produces = {"application/json;charset=UTF-8"})public class UserController { @Inject private IUserService userService; //http://localhost:8080/user/select.do @ResponseBody @RequestMapping(value = "/select.do", method = RequestMethod.GET) public String select() { User user = userService.selectUserById(123); return user.toString(); } //http://localhost:8080/user/add.do @ResponseBody @RequestMapping(value = "/add.do", method = RequestMethod.GET) public String add() { boolean isOk = userService.addUser(new User("333", "444")); return isOk == true ? "shibai" : "chenggong"; }}Simulate reading and writing data and call IUserService.
(2) Spring-db.xml read and write data source configuration
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="statFilter" lazy-init="true"> <property name="logSlowSql" value="true"/> <property name="mergeSql" value="true"/> </bean> <!-- Database Connection--> <bean id="readDataSource" destroy-method="close" init-method="init" lazy-init="true"> <property name="driverClassName" value="${driver}"/> <property name="url" value="${url1}"/> <property name="username" value="root"/> <property name="password" value="${password}"/> <!-- Omit some content--> </bean> <bean id="writeDataSource" destroy-method="close" init-method="init" lazy-init="true"> <property name="driverClassName" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="root"/> <property name="password" value="${password}"/> <!-- Omit some content --> </bean> <!-- Configure dynamically allocated read and write data sources --> <bean id="dataSource" lazy-init="true"> <property name="targetDataSources"> <map key-type="java.lang.String" value-type="javax.sql.DataSource"> <!-- write --> <entry key="write" value-ref="writeDataSource"/> <!-- read --> <entry key="read" value-ref="readDataSource"/> </map> </property> <property name="defaultTargetDataSource" ref="writeDataSource"/> <property name="methodType"> <map key-type="java.lang.String"> <!-- read --> <entry key="read" value=",get,select,count,list,query"/> <!-- write --> <entry key="write" value=",add,create,update,delete,remove,"/> </map> </property> </bean></beans>In the above configuration, readDataSource and writeDataSource are configured, but only dataSource is handed over to SqlSessionFactoryBean for management, and the use of: com.xuliugen.choosedb.demo.aspect.ChooseDataSource This is used for database selection.
<property name="methodType"> <map key-type="java.lang.String"> <!-- read --> <entry key="read" value=",get,select,count,list,query"/> <!-- write --> <entry key="write" value=",add,create,update,delete,remove,"/> </map></property>
The database specific prefix keywords are configured. The specific code of ChooseDataSource is as follows:
(3) ChooseDataSource
/** * Get the data source, used to dynamically switch data sources*/public class ChooseDataSource extends AbstractRoutingDataSource { public static Map<String, List<String>> METHOD_TYPE_MAP = new HashMap<String, List<String>>(); /** * Implement the abstract method in the parent class and get the data source name* @return */ protected Object determineCurrentLookupKey() { return DataSourceHandler.getDataSource(); } // Set the data source corresponding to the method name prefix public void setMethodType(Map<String, String> map) { for (String key : map.keySet()) { List<String> v = new ArrayList<String>(); String[] types = map.get(key).split(","); for (String type : types) { if (StringUtils.isNotBlank(type)) { v.add(type); } } METHOD_TYPE_MAP.put(key, v); } }}(4) DataSourceAspect performs AOP intercept for specific methods
/** * Switch data source (different methods call different data sources) */@Aspect@Component@EnableAspectJAutoProxy(proxyTargetClass = true)public class DataSourceAspect { protected Logger logger = LoggerFactory.getLogger(this.getClass()); @Pointcut("execution(* com.xuliugen.choosedb.demo.mybatis.dao.*.*(..))") public void aspect() { } /** * Configure pre-notifications, use the entry point registered on method aspect()*/ @Before("aspect()") public void before(JoinPoint point) { String className = point.getTarget().getClass().getName(); String method = point.getSignature().getName(); logger.info(className + "." + method + "(" + StringUtils.join(point.getArgs(), ",") + ")"); try { for (String key : ChooseDataSource.METHOD_TYPE_MAP.keySet()) { for (String type : ChooseDataSource.METHOD_TYPE_MAP.get(key)) { if (method.startsWith(type)) { DataSourceHandler.putDataSource(key); } } } } catch (Exception e) { e.printStackTrace(); } }}(5) DataSourceHandler, the Handler class of the data source
package com.xuliugen.choosedb.demo.aspect;/** * Handler class of data source*/public class DataSourceHandler { // Data source name ThreadPool public static final ThreadLocal<String> holder = new ThreadLocal<String>(); /** * Add the configured read and write data sources to the holder when the project starts*/ public static void putDataSource(String datasource) { holder.set(datasource); } /** * Get the data source string from the holer*/ public static String getDataSource() { return holder.get(); }} The main code, as mentioned above.
Code in this article: demo
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.