一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 使用Spring AOP實現MySQL數據庫讀寫分離案例分析(附demo)

使用Spring AOP實現MySQL數據庫讀寫分離案例分析(附demo)

2020-07-21 11:35徐劉根 Java教程

分布式環境下數據庫的讀寫分離策略是解決數據庫讀寫性能瓶頸的一個關鍵解決方案,這篇文章主要介紹了使用Spring AOP實現MySQL數據庫讀寫分離案例分析(附demo),有興趣的可以了解一下。

 一、前言

分布式環境下數據庫的讀寫分離策略是解決數據庫讀寫性能瓶頸的一個關鍵解決方案,更是最大限度了提高了應用中讀取 (Read)數據的速度和并發量。

在進行數據庫讀寫分離的時候,我們首先要進行數據庫的主從配置,最簡單的是一臺Master和一臺Slave(大型網站系統的話,當然會很復雜,這里只是分析了最簡單的情況)。通過主從配置主從數據庫保持了相同的數據,我們在進行讀操作的時候訪問從數據庫Slave,在進行寫操作的時候訪問主數據庫Master。這樣的話就減輕了一臺服務器的壓力。

在進行讀寫分離案例分析的時候。首先,配置數據庫的主從復制,MySQL5.6 數據庫主從(Master/Slave)同步安裝與配置詳解

當然,只是簡單的為了看一下如何用代碼的方式實現數據庫的讀寫分離,完全不必要去配置主從數據庫,只需要兩臺安裝了 相同數據庫的機器就可以了。

二、實現讀寫分離的兩種方法

具體到開發中,實現讀寫分離常用的有兩種方式:

1、第一種方式是我們最常用的方式,就是定義2個數據庫連接,一個是MasterDataSource,另一個是SlaveDataSource。更新數據時我們讀取MasterDataSource,查詢數據時我們讀取SlaveDataSource。這種方式很簡單,我就不贅述了。

2、第二種方式動態數據源切換,就是在程序運行時,把數據源動態織入到程序中,從而選擇讀取主庫還是從庫。主要使用的技術是:Annotation,spring AOP ,反射。

下面會詳細的介紹實現方式。

三、Aop實現主從數據庫的讀寫分離案例

1、項目代碼地址

目前該Demo的項目地址:demo

2、項目結構

使用Spring AOP實現MySQL數據庫讀寫分離案例分析(附demo)

上圖中,除了標記的代碼,其他的主要是配置代碼和業務代碼。

3、具體分析

該項目是SSM框架的一個demo,Spring、Spring MVC和MyBatis,具體的配置文件不在過多介紹。

(1)UserContoller模擬讀寫數據

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
 * 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讀寫數據源配置

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?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兩個數據源,但是交給SqlSessionFactoryBean進行管理的只有dataSource,其中使用到了:com.xuliugen.choosedb.demo.aspect.ChooseDataSource 這個是進行數據庫選擇的。

?
1
2
3
4
5
6
7
8
<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>

配置了數據庫具體的那些是讀哪些是寫的前綴關鍵字。ChooseDataSource的具體代碼如下:

(3)ChooseDataSource

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
 * 獲取數據源,用于動態切換數據源
 */
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攔截

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
 * 切換數據源(不同方法調用不同數據源)
 */
@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類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://blog.csdn.net/xlgen157387/article/details/53930382

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 成人福利影院 | 特级淫片欧美高清视频蜜桃 | 97爱sese | 小妇人电影免费完整观看2021 | 国产小视频免费看 | 2020年国产精品午夜福利在线观看 | 天天做天天爱天天综合网 | 欧美三级小视频 | 很黄的孕妇a级黄毛片 | www.av免费 | 亚洲 欧美 国产 综合首页 | 日韩毛片在线视频 | 99精品久久久久久 | 国产亚洲sss在线播放 | 91韩国女主播 | 狠狠狠地啪香蕉 | 欧美高清无砖专区欧美精品 | 精品日本一区二区 | chinese壮直男gay老年人 | 日韩欧美推理片免费在线播放 | 国产福利在线观看第二区 | 美女脱了内裤打开腿让人桶网站o | 国产精品视频免费一区二区三区 | 精品无人区一区二区三区 | 青青青青青国产免费手机看视频 | 97精品国产高清在线看入口 | 亚洲国产精品久久久久 | 四虎麻豆国产精品 | 亚洲 在线 日韩 欧美 | 痴mu动漫成年动漫在线观看 | 艾秋麻豆果冻剧传媒在线播放 | 久久亚洲午夜牛牛影视 | 午夜影院免费体验 | 日韩精品视频福利资源站 | 成人免费国产欧美日韩你懂的 | 青青青草国产线观 | 久久亚洲成a人片 | poronovideos变态极限 | 国产香蕉视频在线观看 | 大陆国产精品视频 | 性欧美4khdxxxx|