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


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

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

下圖是目前的一個現狀:

單庫讀寫操作

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

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

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

主從架構

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

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

偽代碼如下:

// 主數據源
@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;

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

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

<dependency>
	<groupId>org.apache.shardingsphere</groupId>
	<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
	<version>4.0.0-RC1</version>
</dependency>

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

# 數據源名稱集合,對應下面數據源配置的名稱
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();
}

猿天地


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM