簡單好用!利用Spring AOP技術10分鐘實現一個數據庫讀寫分離方案

前言

最近我們的APP在線用戶越來越多,接口的響應速度也是越來越慢,經過運維排查發現是由於併發查詢太多導致的數據庫壓力比較大,架構師經過調研給出了數據庫讀寫分離的解決方案,爲了快速解決問題,我們最終採用AOP技術實現了數據庫讀寫分離方案。

目錄

  1. 什麼是數據庫讀寫分離以及爲什麼要讀寫分離?
  2. 數據庫讀寫分離實現方式及優缺點分析
  3. 用AOP實現的數據庫讀寫分離方案
  4. 總結

什麼是數據庫讀寫分離以及爲什麼要讀寫分離?

數據庫讀寫分離,顧名思義就是將數據庫的讀操作和寫操作分開。

傳統項目架構中,所有的操作都在同一個數據庫執行,在併發量很小的時候這並不會出現什麼問題,但是在併發量很大時單個數據庫就會出現性能問題。

假設單個數據庫的QPS爲1000,數據庫的併發量爲2000,狼多肉少的情況下接口響應速度變慢也就不足爲怪了。

數據庫讀寫分離方案的出現就是爲了解決這個問題的。

簡單好用!利用Spring AOP技術10分鐘實現一個數據庫讀寫分離方案

項目採用數據庫讀寫分離方案之後共有4個數據庫,其中1個主數據庫和3個從數據庫,主從之間採用binlog文件的方式進行數據同步。主數據庫負責處理所有的寫操作,從數據庫異步進行數據同步;所有的讀操作會平均分散到3個從數據庫上執行。

數據庫讀寫分離之後,主數據庫處理寫操作,多個從數據庫組成集羣共同提供查詢服務,原本大併發量的查詢操作將被平均分到3個數據庫上,從而降低了每個數據庫的併發,提升額查詢效率;同時還將數據做了冗餘備份,增加了系統的抗風險能力。

數據庫讀寫分離實現方式及優缺點分析

根據解決方式所在的層面不同,數據庫讀寫分離主要有3種解決方案:

1、應用層面

實現方式主要是在應用層加一個數據源路由層,將查詢操作路由到從庫,將寫入操作路由到主庫。

優點:方案實現起來比較輕便、路由策略可自由控制,擴展性強。

缺點:功能有限,個人要實現完整的方案難度較大,且該方案與代碼強耦合,跨語言不通用。

2、中間件層面

實現方式是在應用和數據庫之間加一層代理,由代理來轉發操作請求。像阿里的MyCat、360的Atlas、美團的DBProxy等都是此類實現方案。

優點:解決方案與應用層代碼解耦,通用性較好。

缺點:應用和數據庫之間增加了代理層,連接由直連改爲代理會導致性能有所下降。

3.數據庫驅動層面

實現方式是提供一個支持數據庫讀寫分離的驅動。像Mysql自帶的ReplicationDriver驅動、噹噹開源的Sharding-JDBC、淘寶開源的TDDL等解決方案都是基於數據庫驅動層面實現的。

優點:功能豐富、集成方式較簡單,應用層面改動較小。

缺點:需使用特定數據庫驅動,擴展性較低。

用AOP實現的數據庫讀寫分離方案

採用這個方案一是因爲足夠輕便,不需要額外增加什麼東西;二是因爲AOP技術門檻較低,實現起來比較快速。

先介紹一下我們項目的技術背景,項目採用SpringBoot框架開發,採用Mybatis作爲ORM層,請求的調用鏈一般就是Controller調用Service,Service調用Mapper。

方案架構圖:

簡單好用!利用Spring AOP技術10分鐘實現一個數據庫讀寫分離方案

主要原理:

一般來講,我們對於方法的命名會遵循一定的規範,比如插入方法我們會以insert或者save等關鍵字開頭,更新方法我們會以update或edit等關鍵字開頭,查詢方法則會以select或find等關鍵字開頭。這種命名習慣可以很好的達到代碼自解釋性,讓人一眼就能望文知意。

我們正好可以利用這個特點來自定義一個AOP切面切到所有Service實現類裏的所有方法,再根據方法開頭的關鍵字來切換不同的數據源,最終達到寫入操作路由到主數據庫,查詢操作路由到三臺從庫的目的。

實現步驟:

1、定義並配置好主數據庫和從數據庫數據源信息。

簡單好用!利用Spring AOP技術10分鐘實現一個數據庫讀寫分離方案

2、將Mybatis默認使用的數據源改成我們自定義的可動態切換的RoutingDataSource。

簡單好用!利用Spring AOP技術10分鐘實現一個數據庫讀寫分離方案

在RoutingDataSource裏,我們將關鍵字與數據源一一對應起來存入map,key-value對應關係:

write->writeDatasource

read0->read1Datasource

read1->read2Datasource

read2->read3Datasource

3、繼承Spring框架的AbstractRoutingDataSource抽象類自定義一個RoutingDataSource類,並實現determineCurrentLookupKey方法,該方法就是我們實現數據源切換的關鍵所在。

簡單好用!利用Spring AOP技術10分鐘實現一個數據庫讀寫分離方案

determineCurrentLookupKey方法中,我們從DataSourceContext中獲取數據源對應的key值。

4、看一下DataSourceContext裏面的邏輯:

簡單好用!利用Spring AOP技術10分鐘實現一個數據庫讀寫分離方案

在DataSourceContext裏我們定義了一個ThreadLocal變量用來保存該線程所使用的的數據源信息,並且對外提供了兩個切換數據源的方法switchToReadDatasource和switchToWriteDatasource。

5、最後來看一下AOP切面類:

簡單好用!利用Spring AOP技術10分鐘實現一個數據庫讀寫分離方案

我們使用@Before註解定義2個切入點,在方法執行前進行切換數據源的增強處理。

在攔截到以select、get、find等關鍵字開頭的讀方法時我們切換至讀數據源,攔截到以insert、update、delete等關鍵字開頭的寫方法時我們切換至寫數據源。

寫個測試類來測試一下:

簡單好用!利用Spring AOP技術10分鐘實現一個數據庫讀寫分離方案

我們先插入一條數據、再來讀取這條數據,看下執行結果:

簡單好用!利用Spring AOP技術10分鐘實現一個數據庫讀寫分離方案

可以看到,對於insert操作程序自動切換到了write數據源,對於select操作程序又隨機切換到了read2數據源。

總結

一般來講大部分項目都是讀多寫少,數據量和併發量上去之後,單個數據庫往往會面臨比較大的性能壓力,數據庫讀寫分離方案正好適用於這種讀多寫少的應用場景

讀寫分離更多的是擴展了數據庫的讀能力,多個數據庫共同分擔讀操作可以顯著提高系統併發能力。

任何方案有優點肯定也會有缺點,讀寫分離也不例外。讀寫分離需要對數據庫進行主從架構的改造,增加了系統的複雜性,同時主從之間數據同步也會存在延時,對於需要查詢精確數據的業務來說可能並不合適。

對於輕量級的業務來講,如果需要快速實現數據庫的讀寫分離可以採用AOP自己實現讀寫分離方案;如果有足夠的人力和技術,可以從系統層面對項目進行讀寫分離的改造,個人傾向於採用驅動層的一些開源解決方案,如Sharding-JDBC。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章