First, preface
The database reading and writing separation strategy in distributed environment is a key solution to solve the bottleneck of database reading and writing performance, and it is to maximize the speed and concurrency of reading (read) data in the application.
In the database read and write separation, we first have to do the master and slave configuration of the database, the simplest is a master and a slave (large site system, of course, it will be very complex, here is only the simplest case analysis). The master-slave database maintains the same data through the master-slave configuration, we access the slave from the database during the read operation and access the primary database master while the write is in progress. This reduces the pressure on a single server.
When conducting a read-write separation case analysis. First, the master-slave replication of the configuration database, the following are two methods (optional):
1, MySQL5.6 database Master-Slave (Master/slave) synchronous installation and configuration of the detailed
2. Use the Mysqlreplicate command to quickly build Mysql master-slave replication
Of course, simply to see how the code to implement the database read and write separation, completely do not have to configure the master-slave database, only two installed the same database machine.
Two ways to realize the separation of reading and writing
In development, there are two common ways to achieve read-write separation:
1, the first way is our most common way, is to define 2 database connections, one is Masterdatasource, the other is Slavedatasource. When we update the data we read Masterdatasource, we read the Slavedatasource when we query the data. This is a very simple way, I will not repeat it.
2, the second way Dynamic Data source switching, that is, when the program is running, the data source is dynamically woven into the program, so as to choose to read the main library or from the library. The main techniques used are: annotation,spring AOP, Reflection.
The implementation method is described in detail below.
Third, AOP implementation of master-slave database read and write separation cases
1. Project Code Address
At present, the project address of the demo is open source China code Cloud Top: Http://git.oschina.net/xuliugen/aop-choose-db-demo
or csdn free Download:
http://download.csdn.net/detail/u010870518/9724872
2. Project Structure
In addition to the tagged code, the other is primarily the configuration code and the business code.
3. Specific analysis
The project is a demo,spring, Spring MVC, and MyBatis for the SSM framework, and the specific configuration file is not too much to describe.
(1) Usercontoller analog read-write data
/** * Created by Xuliugen on 2016/5/4. * *@Controller@RequestMapping(Value ="/user", produces = {"Application/json;charset=utf-8"}) Public class usercontroller { @Inject PrivateIuserservice UserService;//http://localhost:8080/user/select.do @ResponseBody @RequestMapping(Value ="/select.do", method = Requestmethod.get) PublicStringSelect() {User user = Userservice.selectuserbyid (123);returnUser.tostring (); }//http://localhost:8080/user/add.do @ResponseBody @RequestMapping(Value ="/add.do", method = Requestmethod.get) PublicStringAdd() {BooleanIsOk = Userservice.adduser (NewUser ("333","444"));returnIsOk = =true?"Shibai":"Chenggong"; }}
Analog read-write data, call Iuserservice.
(2) Spring-db.xml read-write Data source configuration
<?xml version= "1.0" encoding= "UTF-8"?><beans xmlns =< Span class= "Hljs-value" > "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.x SD HTTP://WWW.SPRINGFRAMEWORK.ORG/SCHEMA/AOP http://www.springframework.org/schema/aop/spring-aop.xsd " <bean id= "statfilter" class=" Com.alibaba.druid.filter.stat.StatFilter " lazy-init=" true "> < property name="Logslowsql" value="true"/> < property name="Mergesql" value="true"/> </Bean> <!--database connection -- <bean id= "readdatasource" class=" Com.alibaba.druid.pool.DruidDataSource "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" class=" Com.alibaba.druid.pool.DruidDataSource "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" class=" Com.xuliugen.choosedb.demo.aspect.ChooseDataSource " 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 two data sources are configured, but only DataSource is assigned to Sqlsessionfactorybean for management, which is used to: com.xuliugen.choosedb.demo.aspect.ChooseDataSource This is the 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 specific ones that are configured for the database are the prefix keywords that read which are written. The specific code for Choosedatasource is as follows:
(3) Choosedatasource
/** * Get data source for dynamic switching of data sources * / Public class choosedatasource extends abstractroutingdatasource { Public Staticmap<string, list<string>> method_type_map =NewHashmap<string, list<string>> ();/** * Implements the abstract method in the parent class, gets the data source name * @return */ protectedObjectDeterminecurrentlookupkey() {returnDatasourcehandler.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 =NewArraylist<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 for specific methods of AOP interception
/** * Switch data sources (different methods call different data sources) */@Aspect@Component@EnableAspectJAutoProxy(Proxytargetclass =true) Public class datasourceaspect { protectedLogger Logger = Loggerfactory.getlogger ( This. GetClass ());@Pointcut("Execution (* com.xuliugen.choosedb.demo.mybatis.dao.*.* (..))") Public void Aspect() { }/** * Configuring the pre-notification, using the Pointcut 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, handler class of data source
PackageCom.xuliugen.choosedb.demo.aspect;/** * Handler class for data sources * / Public class datasourcehandler { //Data source name thread pool Public Static Finalthreadlocal<string> holder =NewThreadlocal<string> ();/** * Add the configured read and write data source to holder when the project is started */ Public Static void Putdatasource(String DataSource) {Holder.set (DataSource); }/** * Get the data source string from Holer * / Public StaticStringGetdatasource() {returnHolder.get (); }}
The main code, as described above.
Using spring AOP to implement MySQL database read-write separation case analysis