Spring AOP 를 사용 하여 MySQL 데이터베이스 읽 기와 쓰기 분리 사례 분석 실현(데모 첨부)

머리말
분포 식 환경 에서 데이터 베 이 스 를 읽 고 쓰 는 분리 전략 은 데이터 베 이 스 를 읽 고 쓰 는 성능 병목 을 해결 하 는 관건 적 인 해결 방안 으로 응용 에서 데 이 터 를 읽 는 속도 와 병행 량 을 최대한 향상 시 켰 다.
데이터베이스 읽 기와 쓰기 분 리 를 할 때 우 리 는 먼저 데이터베이스 의 주종 설정 을 해 야 한다.가장 간단 한 것 은 Master 한 대 와 Slave 한 대(대형 사이트 시스템 이 라면 당연히 복잡 할 것 이다.여 기 는 가장 간단 한 상황 만 분석 했다)이다.주종 설정 을 통 해 데이터베이스 에서 같은 데 이 터 를 유 지 했 습 니 다.저 희 는 읽 기 작업 을 할 때 데이터베이스 Slave 에 접근 하고 쓰기 작업 을 할 때 주 데이터베이스 Master 에 접근 합 니 다.이렇게 하면 서버 한 대의 스트레스 를 줄 일 수 있다.
읽 기와 쓰기 분리 사례 분석 을 할 때우선,데이터베이스 의 주종 복 제 를 설정 합 니 다MySQL 5.6 데이터베이스 주종(Master/Slave)동기 화 설치 및 설정 상세 설명
물론 코드 로 데이터베이스 의 읽 기와 쓰기 분 리 를 어떻게 실현 하 는 지 간단하게 보기 위해 주종 데이터 베 이 스 를 설정 할 필요 가 없고 같은 데이터 베 이 스 를 설치 한 기계 두 대만 있 으 면 된다.
2.읽 기와 쓰기 의 분 리 를 실현 하 는 두 가지 방법
구체 적 으로 개발 과정 에서 읽 기와 쓰기 분 리 를 실현 하 는 데 자주 사용 되 는 두 가지 방식 이 있다.
1.첫 번 째 방식 은 우리 가 가장 자주 사용 하 는 방식 이다.바로 2 개의 데이터 베이스 연결 을 정의 하 는 것 이다.하 나 는 MasterDataSource 이 고 다른 하 나 는 SlaveDataSource 이다.데 이 터 를 업데이트 할 때 MasterDataSource 를 읽 고 데 이 터 를 조회 할 때 SlaveDataSource 를 읽 습 니 다.이런 방식 은 매우 간단 해서 나 는 군말 하지 않 겠 다.
2.두 번 째 방식 은 동적 데이터 원본 전환 입 니 다.프로그램 이 실 행 될 때 데이터 원본 을 프로그램 에 동적 으로 짜 서 메 인 라 이브 러 리 를 읽 을 지,라 이브 러 리 에서 읽 을 지 선택 하 는 것 입 니 다.주로 사용 되 는 기술 은 Annotation,spring AOP,반사 이다.
실현 방식 을 상세히 소개 하 겠 습 니 다.
3.Aop 은 주종 데이터베이스 의 읽 기와 쓰기 분리 사례 를 실현 한다.
1.프로젝트 코드 주소
현재 이 데모 의 프로젝트 주소:demo
2.프로젝트 구조

위의 그림 에서 표 시 된 코드 를 제외 하고 다른 것 은 주로 설정 코드 와 업무 코드 이다.
3.구체 적 인 분석
이 프로젝트 는 SSM 프레임 워 크 의 demo,Spring,Spring MVC,MyBatis 로 구체 적 인 프로필 은 많이 소개 되 지 않 습 니 다.
(1)UserContoller 아 날로 그 읽 기와 쓰기 데이터

/**
 * 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";
 }
}

아 날로 그 읽 기와 쓰기 데이터,IUserService 호출.
(2)spring-db.xml 읽 기 데이터 원본 설정

<?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" class="com.alibaba.druid.filter.stat.StatFilter" lazy-init="true">
  <property name="logSlowSql" value="true"/>
  <property name="mergeSql" value="true"/>
 </bean>

 <!--       -->
 <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}"/>
   <!--        -->
 </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}"/>
  <!--        -->
 </bean>

 <!--               -->
 <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>

위 설정 에 서 는 readDataSource 와 writeDataSource 두 개의 데이터 원본 을 설정 하 였 으 나 SqlSession Factory Bean 에 게 맡 겨 관리 하 는 것 은 dataSource 뿐 입 니 다.그 중에서 사용 되 는 것 은 com.xuliugen.choosedb.demo.aspect.Choose DataSource 입 니 다.이것 은 데이터 베 이 스 를 선택 하 는 것 입 니 다.

<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>
데이터 베 이 스 를 설정 하 였 습 니 다.Choose DataSource 의 구체 적 인 코드 는 다음 과 같 습 니 다.
(3)ChooseDataSource

/**
 *      ,         
 */
public class ChooseDataSource extends AbstractRoutingDataSource {

 public static Map<String, List<String>> METHOD_TYPE_MAP = new HashMap<String, List<String>>();

 /**
  *           ,       
  * @return
  */
 protected Object determineCurrentLookupKey() {
  return DataSourceHandler.getDataSource();
 }

 //              
 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 의 구체 적 인 방법 인 AOP 차단

/**
 *      (           )
 */
@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() {
 }

 /**
  *       ,     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 클래스

package com.xuliugen.choosedb.demo.aspect;

/**
 *     Handler 
 */
public class DataSourceHandler {

 //         
 public static final ThreadLocal<String> holder = new ThreadLocal<String>();

 /**
  *              、      holder 
  */
 public static void putDataSource(String datasource) {
  holder.set(datasource);
 }

 /**
  *  holer         
  */
 public static String getDataSource() {
  return holder.get();
 }
}

주요 코드 는 상술 한 바 와 같다.
본문 코드:demo
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기