欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot集成Sharding Jdbc使用復(fù)合分片的實踐

 更新時間:2021年09月26日 11:45:36   作者:carl-zhao  
數(shù)據(jù)庫分庫分表中間件是采用的 apache sharding。本文主要介紹了SpringBoot集成Sharding Jdbc使用復(fù)合分片的實踐,具有一定的參考價值,感興趣的可以了解一下

最近主要的工作重心是數(shù)據(jù)庫的容量規(guī)劃。

隨著業(yè)務(wù)的逐漸增大,原有保存在單表的數(shù)據(jù)量也日益增強。數(shù)據(jù)庫數(shù)據(jù)會隨著業(yè)務(wù)的發(fā)展而不斷增多,因此數(shù)據(jù)操作,如增刪改查的開銷也會越來越大。再加上物理服務(wù)器的資源有限(CPU、磁盤、內(nèi)存、IO 等)。最終數(shù)據(jù)庫所能承載的數(shù)據(jù)量、數(shù)據(jù)處理能力都將遭遇瓶頸。換句話說需要合理的數(shù)據(jù)庫架構(gòu)來存放不斷增長的數(shù)據(jù),這個就是分庫分表的設(shè)計初衷。目的就是為了緩解數(shù)據(jù)庫的壓力,大限度提高數(shù)據(jù)操作的效率。

數(shù)據(jù)庫分庫分表中間件是采用的 apache sharding。

1、Sharing JDBC 簡介

ShardingSphere是一套開源的分布式數(shù)據(jù)庫中間件解決方案組成的生態(tài)圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(計劃中)這3款相互獨立的產(chǎn)品組成。 他們均提供標(biāo)準(zhǔn)化的數(shù)據(jù)分片、分布式事務(wù)和數(shù)據(jù)庫治理功能,可適用于如Java同構(gòu)、異構(gòu)語言、云原生等各種多樣化的應(yīng)用場景。

ShardingSphere定位為關(guān)系型數(shù)據(jù)庫中間件,旨在充分合理地在分布式的場景下利用關(guān)系型數(shù)據(jù)庫的計算和存儲能力,而并非實現(xiàn)一個全新的關(guān)系型數(shù)據(jù)庫。 它與NoSQL和NewSQL是并存而非互斥的關(guān)系。NoSQL和NewSQL作為新技術(shù)探索的前沿,放眼未來,擁抱變化,是非常值得推薦的。反之,也可以用另一種思路看待問題,放眼未來,關(guān)注不變的東西,進而抓住事物本質(zhì)。 關(guān)系型數(shù)據(jù)庫當(dāng)今依然占有巨大市場,是各個公司核心業(yè)務(wù)的基石,未來也難于撼動,我們目前階段更加關(guān)注在原有基礎(chǔ)上的增量,而非顛覆。

ShardingSphere已經(jīng)在2020年4月16日從Apache孵化器畢業(yè),成為Apache頂級項目。

2、系統(tǒng)改造

因為我們公司屬于第三方支付平臺,這個改造的點可以分為兩類:提供給商戶調(diào)用的對接系統(tǒng)(比如收銀臺),系統(tǒng)內(nèi)部調(diào)用的系統(tǒng)(支付引擎)。

  • 收銀臺系統(tǒng):核心功能是提供給商戶提交交易訂單,并且對這筆交易訂單進行支付的支付訂單
  • 支付引擎:接收這個支付產(chǎn)品的請求,調(diào)用渠道,記賬,結(jié)算等功能

數(shù)據(jù)源使用分庫分表,在 Sharding JDBC 當(dāng)中如果進行 修改、刪除、查詢操作中沒有包含分片鍵就會進行全表掃描。所以在進行業(yè)務(wù)改造的時候?qū)υ械臄?shù)據(jù)庫操作都進行了業(yè)務(wù)優(yōu)化,基本改造后的所有的操作都使用了基于分片鍵進行操作(定時任務(wù)除外)。

2.1 對接外部系統(tǒng)的系統(tǒng)

首先討論一下,提供給商戶調(diào)用的系統(tǒng)。在進行下單操作的時候,商戶必須傳遞商戶號和外部訂單號。對于外部訂單號第三方支付系統(tǒng)無法控制,只需要商戶每次傳遞過來的時候與歷史的外部訂單號不重復(fù)就可以了。所以這里就涉及到一張映射表,這個表的主要功能如下:

  • 把商戶的外部訂單號映射成內(nèi)部訂單號
  • 通過商戶號與商戶的外部訂單號在數(shù)據(jù)庫聯(lián)合唯一達到冪等處理
  • 保存商戶請求的原始數(shù)據(jù),做為請求憑證

這個時候?qū)灰子唵尉鸵蕾囉谕獠坑成浔恚颜埱笥成涑蓛?nèi)部訂單號進行分片就可以了

2.2 內(nèi)部系統(tǒng)間的調(diào)用

當(dāng)商戶下好了交易訂單的時候,需要進行支付這個時候就產(chǎn)生了一筆支付訂單。交易訂單和支付訂單是一對多的關(guān)系。當(dāng)用戶進行支付的時候會調(diào)用支付引擎,這個時候正常情況下一般會生成支付系統(tǒng)的支付訂單。然后支付引擎會調(diào)用后續(xù)的渠道、結(jié)算、記賬等系統(tǒng),系統(tǒng)之間的調(diào)用圖如下:

在這里插入圖片描述

如果以支付系統(tǒng)的支付訂單的訂單號做為分片鍵時:

  • 支付引擎的內(nèi)部系統(tǒng)可以使用分片鍵查詢,會路由到具體的庫表當(dāng)中,沒有問題
  • 渠道、結(jié)算、記賬等系統(tǒng)如果涉及到回調(diào)支付引擎,在調(diào)用的時候會把支付引擎的支付單號傳遞給后續(xù)系統(tǒng),如果進行回調(diào)操作時候,可以回傳這個支付單號。會路由到具體的庫表當(dāng)中,沒有問題
  • 收銀臺需要根據(jù)交易的支付訂單查詢支付引擎生成的支付單。由于不是根據(jù)分片鍵查詢,不能路由到具體的庫中的具體表上,會進行全表掃描。就會有問題。

3、解決方案

首先想到的方案可以參考收銀臺系統(tǒng),把收銀臺調(diào)用支付引擎看到外部調(diào)用。然后添加一張映射表,把收銀臺生成的支付流水號與支付引擎的支付單號關(guān)聯(lián)起來。當(dāng)收銀臺需要查詢支付引擎時,可以先通過映射表查詢到具體的支付單號,這樣就可以進行分片鍵操作數(shù)據(jù)源了。這個方案存在一個問題存在以下幾個問題:

引入了關(guān)聯(lián)表,添加了系統(tǒng)復(fù)雜度進行數(shù)據(jù)查詢的時候會兩次查詢,先查詢映射表,然后再查詢支付單

那么有沒有其它方案呢?答案是肯定的。

我們來看一下收銀臺、支付引擎其實這兩個系統(tǒng)在支付系統(tǒng)中是同一個緯度的。如果收銀臺的交易訂單進行支付的時候,就會在支付引擎當(dāng)中下一筆支付單。我們可以把交易單與支付單在同一個水平緯度上進行數(shù)據(jù)庫拆分。

什么叫同一個緯度的數(shù)據(jù)庫拆分呢?

其實就是收銀臺的支付訂單進行分庫分表之后,這條數(shù)據(jù)落在數(shù)據(jù)庫里面的哪一個庫,哪一張表就一定了。這個時候支付引擎就可以通過這個單號獲取到具體的庫表信息。這樣就可以把支付引擎生成的的訂單號帶個具體的庫表信息。然后在進行分庫分表算法定義的時候根據(jù)支付引擎生成的訂單號中帶的庫表信息路由到具體的庫表中去了。就樣就會解決上面的問題,不需要映射表。同時這種方案也會帶來以下的問題:

  • 數(shù)據(jù)上游與下游的分庫分表必須一致
  • 數(shù)據(jù)在進行再次擴容會有其它問題

經(jīng)過討論決定使用方案二。

4、代碼實現(xiàn)

下面通過 Sharding jdbc 的復(fù)合分片簡單的模擬代碼實現(xiàn)。數(shù)據(jù)庫、表準(zhǔn)備:

數(shù)據(jù)庫:
- order_0
- order_1

每個數(shù)據(jù)庫的表:
tb_order_0
tb_order_1
tb_order_2
tb_order_3
tb_order_4
tb_order_5
tb_order_6
tb_order_7

# 邏輯表
create table tb_order
(
    trade_master_no varchar(16),
    pay_order_no    varchar(16) ,
);

# 準(zhǔn)備數(shù)據(jù)
# 分庫分表規(guī)則是前一位代表庫,后一位代表表,所以在 order_1.tb_order_1 中添加以下數(shù)據(jù)
insert into tb_order_1 values('11', '11'),

4.1 Sharding JDBC 配置

下面是針對訂單表的 sharding jdbc 的分庫分表配置,數(shù)據(jù)庫連接池使用 Hikari 。分片規(guī)則:前一位代表庫,后一位代表表。使用交易主單號(trade_master_no) 和 支付單號(pay_order_no) 作為復(fù)合分片。當(dāng)查詢條件中只要包含一個查詢規(guī)則時就會路由到具體庫表中。

ComplexShardingJDBCConfig.java

@Configuration
public class ComplexShardingJDBCConfig {

	@Bean
	public DataSource getShardingDataSource(HikariCommonConfig commonConfig) throws SQLException {
		ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
		shardingRuleConfig.getTableRuleConfigs().add(getShardingMessageTableRuleConfiguration());
		Map<String, DataSource> dataSourceMap = new HashMap<>();
		dataSourceMap.put("order_0", createDataSource(datasourceOne(commonConfig)));
		dataSourceMap.put("order_1", createDataSource(datasourceTwo(commonConfig)));
		Properties properties = new Properties();
		properties.setProperty(ShardingPropertiesConstant.SQL_SHOW.getKey(), "true");
		return ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, properties);
	}

	private TableRuleConfiguration getShardingMessageTableRuleConfiguration() {
		TableRuleConfiguration shardingMessageConfiguration = new TableRuleConfiguration("tb_order", "order_${0..1}.tb_order_${0..7}");
		shardingMessageConfiguration.setDatabaseShardingStrategyConfig(messageDatasourceShardingStrategyConfig());
		shardingMessageConfiguration.setTableShardingStrategyConfig(messageTableShardingStrategyConfig());
		return shardingMessageConfiguration;
	}

	private ComplexShardingStrategyConfiguration messageDatasourceShardingStrategyConfig(){
		return new ComplexShardingStrategyConfiguration("trade_master_no,pay_order_no", new OrderDatasourceComplexKeysShardingAlgorithm());
	}

	private ShardingStrategyConfiguration messageTableShardingStrategyConfig() {
		return new ComplexShardingStrategyConfiguration("trade_master_no,pay_order_no", new OrderTableComplexKeysShardingAlgorithm());
	}

	@Bean
	@ConfigurationProperties(prefix = "spring.datasource.ds1")
	public HikariConfig datasourceOne(HikariCommonConfig commonConfig){
		HikariConfig hikariConfig = new HikariConfig();
		hikariConfig.setMinimumIdle(commonConfig.getMinimumIdle());
		hikariConfig.setIdleTimeout(commonConfig.getIdleTimeout());
		hikariConfig.setMaximumPoolSize(commonConfig.getMaximumPoolSize());
		hikariConfig.setPoolName(commonConfig.getPoolName());
		hikariConfig.setMaxLifetime(commonConfig.getMaxLifetime());
		hikariConfig.setConnectionTimeout(commonConfig.getConnectionTimeout());
		hikariConfig.setConnectionTestQuery(commonConfig.getConnectionTestQuery());
		return hikariConfig;
	}

	@Bean
	@ConfigurationProperties(prefix = "spring.datasource.ds2")
	public HikariConfig datasourceTwo(HikariCommonConfig commonConfig){
		HikariConfig hikariConfig = new HikariConfig();
		hikariConfig.setMinimumIdle(commonConfig.getMinimumIdle());
		hikariConfig.setIdleTimeout(commonConfig.getIdleTimeout());
		hikariConfig.setMaximumPoolSize(commonConfig.getMaximumPoolSize());
		hikariConfig.setPoolName(commonConfig.getPoolName());
		hikariConfig.setMaxLifetime(commonConfig.getMaxLifetime());
		hikariConfig.setConnectionTimeout(commonConfig.getConnectionTimeout());
		hikariConfig.setConnectionTestQuery(commonConfig.getConnectionTestQuery());
		return hikariConfig;
	}

	private HikariDataSource createDataSource(HikariConfig hikariConfig) {
		HikariDataSource sharding = new HikariDataSource();
		BeanUtils.copyProperties(hikariConfig, sharding);
		return sharding;
	}

}

數(shù)據(jù)庫分片規(guī)則:

public class OrderDatasourceComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm<String> {

	@Override
	public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<String> shardingValue) {
		Map<String, Collection<String>> columnNameAndShardingValuesMap = shardingValue.getColumnNameAndShardingValuesMap();
		if(columnNameAndShardingValuesMap.containsKey("trade_master_no")){
			Collection<String> tradeMasterNos = columnNameAndShardingValuesMap.get("trade_master_no");
			String tradeMasterNo = tradeMasterNos.iterator().next();
			String datasourceSuffix = tradeMasterNo.substring(0, 1);
			for (String availableTargetName : availableTargetNames) {
				if(availableTargetName.endsWith(datasourceSuffix)){
					return Lists.newArrayList(availableTargetName);
				}
			}
		}
		if(columnNameAndShardingValuesMap.containsKey("pay_order_no")){
			Collection<String> payOrderNos = columnNameAndShardingValuesMap.get("pay_order_no");
			String payOrderNo = payOrderNos.iterator().next();
			String datasourceSuffix = payOrderNo.substring(0, 1);
			for (String availableTargetName : availableTargetNames) {
				if(availableTargetName.endsWith(datasourceSuffix)){
					return Lists.newArrayList(availableTargetName);
				}
			}
		}
		throw new UnsupportedOperationException();
	}

}

數(shù)據(jù)庫中的表分片規(guī)則:

public class OrderTableComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm<String> {

	@Override
	public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<String> shardingValue) {
		Map<String, Collection<String>> columnNameAndShardingValuesMap = shardingValue.getColumnNameAndShardingValuesMap();
		if(columnNameAndShardingValuesMap.containsKey("trade_master_no")){
			Collection<String> tradeMasterNos = columnNameAndShardingValuesMap.get("trade_master_no");
			String tradeMasterNo = tradeMasterNos.iterator().next();
			String datasourceSuffix = tradeMasterNo.substring(1, 2);
			for (String availableTargetName : availableTargetNames) {
				if(availableTargetName.endsWith(datasourceSuffix)){
					return Lists.newArrayList(availableTargetName);
				}
			}
		}
		if(columnNameAndShardingValuesMap.containsKey("pay_order_no")){
			Collection<String> payOrderNos = columnNameAndShardingValuesMap.get("pay_order_no");
			String payOrderNo = payOrderNos.iterator().next();
			String datasourceSuffix = payOrderNo.substring(1, 2);
			for (String availableTargetName : availableTargetNames) {
				if(availableTargetName.endsWith(datasourceSuffix)){
					return Lists.newArrayList(availableTargetName);
				}
			}
		}
		throw new UnsupportedOperationException();
	}

}

4.2 數(shù)據(jù)源操作類

這里使用 Mybatis 操作數(shù)據(jù)源,當(dāng)然使用其它 ORM 框架操作數(shù)據(jù)源 sharding jdbc 也是支持的。

public interface OrderMapper {
    int countByExample(OrderExample example);

    int deleteByExample(OrderExample example);

    int insert(Order record);

    int insertSelective(Order record);

    List<Order> selectByExample(OrderExample example);

    int updateByExampleSelective(@Param("record") Order record, @Param("example") OrderExample example);

    int updateByExample(@Param("record") Order record, @Param("example") OrderExample example);
}

4.3 分片測試類

通過 Spring boot 定義一個 Controller,使用 Order 對象查詢。即可以使用交易單號也可以使用支付單號查詢。

@Getter
@Setter
public class Order {

    private String tradeMasterNo;

    private String payOrderNo;

}

@RestController
@RequestMapping("order")
public class OrderController {

	@Resource
	private OrderDao orderDao;

	@RequestMapping("query")
	public Order query(@RequestBody Order order) {
		Order orderInDB = orderDao.queryOrder(order);
		return orderInDB;
	}
}

4.4 測試結(jié)果

由于我們在 sharing jdbc 配置當(dāng)中配置了數(shù)據(jù)庫查詢 SQL,我們只需要觀察是不是只打印了一條數(shù)據(jù)庫操作語句就可以判斷之前的結(jié)論是否正確。

通過 Postman 使用交易單號查詢:

在這里插入圖片描述

控制臺打印:

在這里插入圖片描述

然后通過 Postman 使用支付單號查詢:

在這里插入圖片描述

控制臺打印:

在這里插入圖片描述

它們查詢都是路由到具體的庫表當(dāng)中,說明我們的方案是可以的。

參考文章:

Sharding-JDBC 官網(wǎng)

到此這篇關(guān)于SpringBoot集成Sharding Jdbc使用復(fù)合分片的實踐的文章就介紹到這了,更多相關(guān)SpringBoot集成Sharding Jdbc復(fù)合分片內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • hutool實戰(zhàn):IoUtil 流操作工具類(將內(nèi)容寫到流中)

    hutool實戰(zhàn):IoUtil 流操作工具類(將內(nèi)容寫到流中)

    這篇文章主要介紹了Go語言的io.ioutil標(biāo)準(zhǔn)庫使用,是Golang入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下,如果能給你帶來幫助,請多多關(guān)注腳本之家的其他內(nèi)容
    2021-06-06
  • Java繼承方法重寫實現(xiàn)原理及解析

    Java繼承方法重寫實現(xiàn)原理及解析

    這篇文章主要介紹了Java繼承方法重寫實現(xiàn)原理及解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • java?socket實現(xiàn)局域網(wǎng)聊天

    java?socket實現(xiàn)局域網(wǎng)聊天

    這篇文章主要為大家詳細介紹了java?socket實現(xiàn)局域網(wǎng)聊天,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • springboot ehcache 配置使用方法代碼詳解

    springboot ehcache 配置使用方法代碼詳解

    EhCache是一個比較成熟的Java緩存框架,Springboot對ehcache的使用非常支持,所以在Springboot中只需做些配置就可使用,且使用方式也簡易,今天給大家分享springboot ehcache 配置使用教程,一起看看吧
    2021-06-06
  • Spring如何通過注解存儲和讀取對象詳解

    Spring如何通過注解存儲和讀取對象詳解

    在Spring中,要想更簡單的存儲和讀取對象的核心是使用注解,這篇文章主要給大家介紹了關(guān)于Spring如何通過注解存儲和讀取對象的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-07-07
  • 阿里的Easyexcel讀取Excel文件的方法(最新版本)

    阿里的Easyexcel讀取Excel文件的方法(最新版本)

    這篇文章主要介紹了阿里的Easyexcel讀取Excel文件(最新版本)的方法,本文通過示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-12-12
  • Java如何從List中刪除元素的正確用法舉例

    Java如何從List中刪除元素的正確用法舉例

    這篇文章主要給大家介紹了關(guān)于Java如何從List中刪除元素的正確用法, 列表List是Java中的一種數(shù)據(jù)結(jié)構(gòu),存放按照添加順序的元素,是個有序的集合,需要的朋友可以參考下
    2023-07-07
  • java開發(fā)模式的深度研究

    java開發(fā)模式的深度研究

    下面小編就為大家?guī)硪黄钊肜斫鈐ava工廠模式。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-07-07
  • SpringBoot基于Actuator遠程關(guān)閉服務(wù)

    SpringBoot基于Actuator遠程關(guān)閉服務(wù)

    這篇文章主要介紹了SpringBoot基于Actuator遠程關(guān)閉服務(wù),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-11-11
  • java 反射 動態(tài)調(diào)用不同類的靜態(tài)方法(推薦)

    java 反射 動態(tài)調(diào)用不同類的靜態(tài)方法(推薦)

    下面小編就為大家?guī)硪黄狫AVA 反射 動態(tài)調(diào)用不同類的靜態(tài)方法(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-08-08

最新評論