Home Java javaTutorial Example of Spring implementing database read-write separation

Example of Spring implementing database read-write separation

Jan 24, 2017 am 10:14 AM

Most of the current large-scale e-commerce systems use read-write separation technology at the database level, which is a Master database and multiple Slave databases. The Master library is responsible for data update and real-time data query, and the Slave library is of course responsible for non-real-time data query. Because in actual applications, databases read more and write less (the frequency of reading data is high and the frequency of updating data is relatively small), and reading data usually takes a long time and takes up more CPU of the database server, so Affect user experience. Our usual approach is to extract the query from the main database, use multiple slave databases, and use load balancing to reduce the query pressure on each slave database.

The goal of using read-write separation technology: to effectively reduce the pressure on the Master library, and to distribute user requests for data query to different Slave libraries, thereby ensuring the robustness of the system. Let's look at the background of adopting read-write separation.

As the business of the website continues to expand, the data continues to increase, and there are more and more users, the pressure on the database is increasing. Traditional methods, such as database or SQL optimization, basically cannot meet the requirements. This Sometimes you can use the strategy of separation of reading and writing to change the status quo.

Specifically in development, how can we easily achieve separation of reading and writing? There are currently two commonly used methods:

1 The first method is our most commonly used method, which is to define 2 database connections, one is MasterDataSource, and the other is One is SlaveDataSource. When updating 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, thereby choosing to read from the main library or the slave library. The main technologies used are: annotation, Spring AOP, reflection. The implementation method will be introduced in detail below.

Before introducing the implementation method, we first prepare some necessary knowledge. Spring's AbstractRoutingDataSource class

AbstractRoutingDataSource is added after spring 2.0. Let's first look at the definition of AbstractRoutingDataSource:

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {}
Copy after login

AbstractRoutingDataSource inherits AbstractDataSource. AbstractDataSource is a subclass of DataSource. DataSource is the data source interface of javax.sql, defined as follows:

public interface DataSource extends CommonDataSource,Wrapper {
 
 /**
  * <p>Attempts to establish a connection with the data source that
  * this <code>DataSource</code> object represents.
  *
  * @return a connection to the data source
  * @exception SQLException if a database access error occurs
  */
 Connection getConnection() throws SQLException;
 
 /**
  * <p>Attempts to establish a connection with the data source that
  * this <code>DataSource</code> object represents.
  *
  * @param username the database user on whose behalf the connection is
  * being made
  * @param password the user&#39;s password
  * @return a connection to the data source
  * @exception SQLException if a database access error occurs
  * @since 1.4
  */
 Connection getConnection(String username, String password)
  throws SQLException;
 
}
Copy after login

The DataSource interface defines 2 methods, both of which obtain database connections. Let's take a look at how AbstractRoutingDataSource implements the DataSource interface:

public Connection getConnection() throws SQLException {
    return determineTargetDataSource().getConnection();
  }
 
  public Connection getConnection(String username, String password) throws SQLException {
    return determineTargetDataSource().getConnection(username, password);
  }
Copy after login

It is obvious that it calls its own determineTargetDataSource() method to obtain the connection. The determineTargetDataSource method is defined as follows:

protected DataSource determineTargetDataSource() {
    Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
    Object lookupKey = determineCurrentLookupKey();
    DataSource dataSource = this.resolvedDataSources.get(lookupKey);
    if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
      dataSource = this.resolvedDefaultDataSource;
    }
    if (dataSource == null) {
      throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
    }
    return dataSource;
  }
Copy after login

What we are most concerned about are the following 2 sentences:

Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
Copy after login

determineCurrentLookupKey method returns lookupKey, and the resolvedDataSources method obtains the data source from Map based on lookupKey. resolvedDataSources and determinedCurrentLookupKey are defined as follows:

private Map<Object, DataSource> resolvedDataSources;
 
protected abstract Object determineCurrentLookupKey()
Copy after login

Seeing the above definitions, do we have some ideas? resolvedDataSources is a Map type. We can save MasterDataSource and SlaveDataSource into Map, as follows:

Example of Spring implementing database read-write separation

We are here Write a class DynamicDataSource that inherits AbstractRoutingDataSource and implements its determineCurrentLookupKey() method, which returns the Map key, master or slave.

Okay, I’m a little tired after talking so much. Let’s see how to implement it.

The technology we want to use has been mentioned above. Let’s first look at the definition of annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
  String value();
}
Copy after login

We also need to implement spring’s abstract class AbstractRoutingDataSource, which is to implement the determineCurrentLookupKey method:

public class DynamicDataSource extends AbstractRoutingDataSource {
 
  @Override
  protected Object determineCurrentLookupKey() {
    // TODO Auto-generated method stub
    return DynamicDataSourceHolder.getDataSouce();
  }
 
}
 
 
public class DynamicDataSourceHolder {
  public static final ThreadLocal<String> holder = new ThreadLocal<String>();
 
  public static void putDataSource(String name) {
    holder.set(name);
  }
 
  public static String getDataSouce() {
    return holder.get();
  }
}
Copy after login

From The definition of DynamicDataSource shows that it returns the DynamicDataSourceHolder.getDataSouce() value. We need to call the DynamicDataSourceHolder.putDataSource() method when the program is running to assign a value to it. The following is the core part of our implementation, which is the AOP part. DataSourceAspect is defined as follows:

public class DataSourceAspect {
 
  public void before(JoinPoint point)
  {
    Object target = point.getTarget();
    String method = point.getSignature().getName();
 
    Class<?>[] classz = target.getClass().getInterfaces();
 
    Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
        .getMethod().getParameterTypes();
    try {
      Method m = classz[0].getMethod(method, parameterTypes);
      if (m != null && m.isAnnotationPresent(DataSource.class)) {
        DataSource data = m
            .getAnnotation(DataSource.class);
        DynamicDataSourceHolder.putDataSource(data.value());
        System.out.println(data.value());
      }
       
    } catch (Exception e) {
      // TODO: handle exception
    }
  }
}
Copy after login

In order to facilitate testing, I defined 2 databases, shop simulates the Master library, test simulates the Slave library, and the tables of shop and test The structure is the same, but the data is different. The database configuration is as follows:

<bean id="masterdataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/shop" />
    <property name="username" value="root" />
    <property name="password" value="yangyanping0615" />
  </bean>
 
  <bean id="slavedataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/test" />
    <property name="username" value="root" />
    <property name="password" value="yangyanping0615" />
  </bean>
   
    <beans:bean id="dataSource" class="com.air.shop.common.db.DynamicDataSource">
    <property name="targetDataSources">
       <map key-type="java.lang.String">
         <!-- write -->
         <entry key="master" value-ref="masterdataSource"/>
         <!-- read -->
         <entry key="slave" value-ref="slavedataSource"/>
       </map>
        
    </property>
    <property name="defaultTargetDataSource" ref="masterdataSource"/>
  </beans:bean>
 
  <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>
 
 
  <!-- 配置SqlSessionFactoryBean -->
  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:config/mybatis-config.xml" />
  </bean>
Copy after login

Add aop configuration to the spring configuration

<!-- 配置数据库注解aop -->
  <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  <beans:bean id="manyDataSourceAspect" class="com.air.shop.proxy.DataSourceAspect" />
  <aop:config>
    <aop:aspect id="c" ref="manyDataSourceAspect">
      <aop:pointcut id="tx" expression="execution(* com.air.shop.mapper.*.*(..))"/>
      <aop:before pointcut-ref="tx" method="before"/>
    </aop:aspect>
  </aop:config>
  <!-- 配置数据库注解aop -->
Copy after login

The following is the definition of MyBatis's UserMapper. To facilitate testing, the login read is Master library, user list reads Slave library:

public interface UserMapper {
  @DataSource("master")
  public void add(User user);
 
  @DataSource("master")
  public void update(User user);
 
  @DataSource("master")
  public void delete(int id);
 
  @DataSource("slave")
  public User loadbyid(int id);
 
  @DataSource("master")
  public User loadbyname(String name);
   
  @DataSource("slave")
  public List<User> list();
} 
Copy after login

Okay, run our Eclipse to see the effect, enter the user name admin to log in to see the effect

Spring 实现数据库读写分离的示例

Spring 实现数据库读写分离的示例

The above is the entire content of this article. I hope it will be helpful to everyone’s learning. I also hope that everyone will support the PHP Chinese website.

For more articles related to examples of Spring implementing database read-write separation, please pay attention to the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Hot Topics

Java Tutorial
1659
14
PHP Tutorial
1258
29
C# Tutorial
1232
24
Is the company's security software causing the application to fail to run? How to troubleshoot and solve it? Is the company's security software causing the application to fail to run? How to troubleshoot and solve it? Apr 19, 2025 pm 04:51 PM

Troubleshooting and solutions to the company's security software that causes some applications to not function properly. Many companies will deploy security software in order to ensure internal network security. ...

How do I convert names to numbers to implement sorting and maintain consistency in groups? How do I convert names to numbers to implement sorting and maintain consistency in groups? Apr 19, 2025 pm 11:30 PM

Solutions to convert names to numbers to implement sorting In many application scenarios, users may need to sort in groups, especially in one...

How to simplify field mapping issues in system docking using MapStruct? How to simplify field mapping issues in system docking using MapStruct? Apr 19, 2025 pm 06:21 PM

Field mapping processing in system docking often encounters a difficult problem when performing system docking: how to effectively map the interface fields of system A...

How does IntelliJ IDEA identify the port number of a Spring Boot project without outputting a log? How does IntelliJ IDEA identify the port number of a Spring Boot project without outputting a log? Apr 19, 2025 pm 11:45 PM

Start Spring using IntelliJIDEAUltimate version...

How to elegantly obtain entity class variable names to build database query conditions? How to elegantly obtain entity class variable names to build database query conditions? Apr 19, 2025 pm 11:42 PM

When using MyBatis-Plus or other ORM frameworks for database operations, it is often necessary to construct query conditions based on the attribute name of the entity class. If you manually every time...

How to safely convert Java objects to arrays? How to safely convert Java objects to arrays? Apr 19, 2025 pm 11:33 PM

Conversion of Java Objects and Arrays: In-depth discussion of the risks and correct methods of cast type conversion Many Java beginners will encounter the conversion of an object into an array...

How to use the Redis cache solution to efficiently realize the requirements of product ranking list? How to use the Redis cache solution to efficiently realize the requirements of product ranking list? Apr 19, 2025 pm 11:36 PM

How does the Redis caching solution realize the requirements of product ranking list? During the development process, we often need to deal with the requirements of rankings, such as displaying a...

E-commerce platform SKU and SPU database design: How to take into account both user-defined attributes and attributeless products? E-commerce platform SKU and SPU database design: How to take into account both user-defined attributes and attributeless products? Apr 19, 2025 pm 11:27 PM

Detailed explanation of the design of SKU and SPU tables on e-commerce platforms This article will discuss the database design issues of SKU and SPU in e-commerce platforms, especially how to deal with user-defined sales...

See all articles