Sharding-JDBC:查詢量大如何優化?

Sharding-JDBC:查詢量大如何優化?

主人公小王入職了一家剛起步的創業公司,公司正在研發一款App。為了快速開發出能夠投入市場進行宣傳的版本,小王可是天天加班到很晚,忙了一段時間後終於把第一個版本趕出來了。

初期功能不多,表也不多,用的MySql存儲業務數據。就一個節點,當然每天凌晨有定時備份機制。

下圖是目前的一個現狀:

Sharding-JDBC:查詢量大如何優化?

得益於運營人員的大力推廣,這款App初見成效。註冊用戶越來越多,查詢量越來越大,對於不太會更新的數據小王加上了緩存,又撐了一段時間。

對於某些數據還是要查數據庫,按目前的業務發展,單節點的數據庫已經快滿足不了需求了。而且讀和寫都在一起,小王打算進行一次優化,將數據庫做讀寫分離,一主多從。

下圖是改進後的一個現狀:

Sharding-JDBC:查詢量大如何優化?

將讀請求全部走從節點,主節點只寫入來緩解數據的查詢壓力,數據庫部署這塊正好小王公司有個運維可以搞定,但是應用程序這塊也得支持多數據源才行呀。

小王是個雷厲風行的人,行動力極強,馬上腦袋中就有了方案,配置多個數據源不就行了,然後用不同的數據源進行數據操作就可以了嘛!

偽代碼如下:

// 主數據源
@Bean
(
name 
=
 
"primaryDataSource"
)
@Qualifier
(
"primaryDataSource"
)
//指定數據源配置前綴
@ConfigurationProperties
(
prefix 
=
 
"spring.datasource.primary"
)
public
 
DataSource
 primaryDataSource
()
 
{
 
return
 
DataSourceBuilder
.
create
().
build
();
}
// 從數據源
@Bean
(
name 
=
 
"secondaryDataSource"
)
@Qualifier
(
"secondaryDataSource"
)
@Primary
 
//在同樣的DataSource中,首先使用被標註的DataSource 
@ConfigurationProperties
(
prefix 
=
 
"spring.datasource.secondary"
)
public
 
DataSource
 secondaryDataSource
()
 
{
 
return
 
DataSourceBuilder
.
create
().
build
();
}

假設我們用JdbcTemplate操作數據庫:

@Bean
(
name 
=
 
"primaryJdbcTemplate"
)
public
 
JdbcTemplate
 primaryJdbcTemplate
(
@Qualifier
(
"primaryDataSource"
)
 
DataSource
 dataSource
)
 
{
 
return
 
new
 
JdbcTemplate
(
dataSource
);
}
@Bean
(
name 
=
 
"secondaryJdbcTemplate"
)
public
 
JdbcTemplate
 secondaryJdbcTemplate
(
@Qualifier
(
"secondaryDataSource"
)
 
DataSource
 dataSource
)
 
{
 
return
 
new
 
JdbcTemplate
(
dataSource
);
}

配置完成後我們在操作數據的時候選用不同的JdbcTemplate就可以滿足需求了。有個問題是一旦從節點多了起來,也就意味著會有多個JdbcTemplate,使用的時候是不是還得有個算法,用哪個來操作,比較麻煩。

於是小王找到了我,我這人是個熱心腸。既然找到了我肯定得幫助下,當然我不是幫小王寫代碼,只是給他提供思路+方案。

我對小王說:ShardingSphere知道麼,你用這個吧,比你自己去配多數據源簡單多了。ShardingSphere是後來規劃的,最開始是隻有 Sharding-JDBC 一款產品,基於客戶端形式的分庫分表。後面發展變成了現在的Apache ShardingSphere(Incubator) ,它是一套開源的分佈式數據庫中間件解決方案組成的生態圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(規劃中)這3款相互獨立,卻又能夠混合部署配合使用的產品組成。它們均提供標準化的數據分片、分佈式事務和數據庫治理功能,可適用於如Java同構、異構語言、容器、雲原生等各種多樣化的應用場景。

經過我的指導小王還是順利的用Sharding-JDBC將讀寫分離整出來了,下面給大家分享下步驟。

第一步:創建2個數據庫,模擬一主一從,當然如果你有現成的主從環境更好啦

CREATE DATABASE 
`ds_0`
 CHARACTER SET 
'utf8'
 COLLATE 
'utf8_general_ci'
;
CREATE DATABASE 
`ds_1`
 CHARACTER SET 
'utf8'
 COLLATE 
'utf8_general_ci'
;
CREATE TABLE 
`user`
(
 id bigint
(
64
)
 
not
 
null
,
 city varchar
(
20
)
 
not
 
null
,
 name varchar
(
20
)
 
not
 
null
,
 PRIMARY KEY 
(
`id`
)
)
 ENGINE
=
InnoDB
 DEFAULT CHARSET
=
utf8
;

在ds0和ds1這兩個庫中分別創建一個user表,用於數據操作演示。

第二步:創建一個Maven項目,增加需要的依賴,下面只貼出Sharding-JDBC的,其餘的後面我會給出源碼地址給大家參考:

 

org.apache.shardingsphere

 

sharding-jdbc-spring-boot-starter

 

4.0.0-RC1


第三步:配置讀寫分離的數據源

# 數據源名稱集合,對應下面數據源配置的名稱
spring
.
shardingsphere
.
datasource
.
names
=
master
,
slave
# 主數據源
spring
.
shardingsphere
.
datasource
.
master
.
type
=
com
.
alibaba
.
druid
.
pool
.
DruidDataSource
spring
.
shardingsphere
.
datasource
.
master
.
driver
-
class
-
name
=
com
.
mysql
.
jdbc
.
Driver
spring
.
shardingsphere
.
datasource
.
master
.
url
=
jdbc
:
mysql
:
//localhost:3306/ds_0?characterEncoding=utf-8
spring
.
shardingsphere
.
datasource
.
master
.
username
=
root
spring
.
shardingsphere
.
datasource
.
master
.
password
=
123456
# 從數據源
spring
.
shardingsphere
.
datasource
.
slave
.
type
=
com
.
alibaba
.
druid
.
pool
.
DruidDataSource
spring
.
shardingsphere
.
datasource
.
slave
.
driver
-
class
-
name
=
com
.
mysql
.
jdbc
.
Driver
spring
.
shardingsphere
.
datasource
.
slave
.
url
=
jdbc
:
mysql
:
//localhost:3306/ds_1?characterEncoding=utf-8
spring
.
shardingsphere
.
datasource
.
slave
.
username
=
root
spring
.
shardingsphere
.
datasource
.
slave
.
password
=
123456
# 讀寫分離配置
spring
.
shardingsphere
.
masterslave
.
load
-
balance
-
algorithm
-
type
=
round_robin
# 最終的數據源名稱
spring
.
shardingsphere
.
masterslave
.
name
=
dataSource
# 主庫數據源名稱
spring
.
shardingsphere
.
masterslave
.
master
-
data
-
source
-
name
=
master
# 從庫數據源名稱列表,多個逗號分隔
spring
.
shardingsphere
.
masterslave
.
slave
-
data
-
source
-
names
=
slave

load-balance-algorithm-type用於配置從庫負載均衡算法類型,可選值:ROUND_ROBIN(輪詢),RANDOM(隨機)

配置完成後可以自行插入數據進行查詢和插入的測試,對於應用層使用什麼ORM框架無任何影響,你可以用我們前面講的JdbcTemplate,也可以用Mybatis

測試步驟我就不寫出來了,比較簡單,當然我這邊也提供了測試代碼,僅供參考:

https://github.com/yinjihuan/sharding-jdbc/tree/master/sjdbc-read-write-springboot

覺得不錯的記得給我個Star哦!

還有個問題在讀寫分離架構中經常出現,那就是讀延遲的問題如何解決?

剛插入一條數據,然後馬上就要去讀取,這個時候有可能會讀取不到?

歸根到底是因為主節點寫入完之後數據是要複製給從節點的,讀不到的原因是複製的時間比較長,也就是說數據還沒複製到從節點,你就已經去從節點讀取了,肯定讀不到。

mysql5.7 的主從複製是多線程了,意味著速度會變快,但是不一定能保證百分百馬上讀取到,這個問題我們可以有兩種方式解決:

  1. 業務層面妥協,是否操作完之後馬上要進行讀取
  2. 對於操作完馬上要讀出來的,且業務上不能妥協的,我們可以對於這類的讀取直接走主庫,當然Sharding-JDBC也是考慮到這個問題的存在,所以給我們提供了一個功能,可以讓用戶在使用的時候指定要不要走主庫進行讀取

在讀取前使用下面的方式進行設置就可以了:

public
 
List
<
User
>
 list
()
 
{
 
// 強制路由主庫
 
HintManager
.
getInstance
().
setMasterRouteOnly
();
 
return
 userRepository
.
list
();
}


分享到:


相關文章: