基於 Apache ShardingSphere 構建高可用分散式資料庫
趙錦超,SphereEx 中介軟體研發工程師,Apache ShardingSphere Committer。目前專注於 ShardingSphere 高可用的設計和研發。
什麼是資料庫高可用
在這個以“資料”為王的時代,對現代業務系統可用性、可靠性以及穩定性要求極其高。資料庫又作為現代業務系統的基石,因此資料庫高可用就顯得尤為重要。同時資料庫高可用應具備主從切換以及自動選主的能力,當我們主庫當機時,可以快速地在從節點中選舉最佳節點來作為主庫。
MySQL 高可用介紹
MySQL 高可用方案比較多,且每個高可用方案都有各自的優缺點。在這裡我們列舉幾個常用的高可用方案。
-
Orchestrator 是一款 Go 語言編寫的 MySQL 高可用性和複製拓撲管理工具,優點是支援通過 Web 視覺化控制檯手動的調整主從拓撲結構、自動故障轉移、自動或手動恢復主節點。缺點則是需要獨立部署該程式,學習成本較高配置複雜;
-
MHA 也是一款相對成熟的 MySQL 高可用軟體。支援故障切換和主從提升,其優點是在切換的過程中可以最大程度保證資料不丟失,同時可以工作在半同步複製或非同步複製叢集下。缺點則是 MHA 啟動後只對主庫進行監控,並且讀庫沒有提供負載均衡功能;
-
MGR 是基於分散式 Paxos 協議實現組複製,從而保證資料的一致性。同時也是 MySQL 官方提供解決 MySQL 高可用的元件,使用它我們不需要單獨的部署程式,只需要在每個資料來源節點中安裝 MGR Plugin,其特點具有高一致性、高容錯性、高擴充套件性、高靈活性。
Apache ShardingSphere 高可用
Apache ShardingSphere 採用存算分離架構體系,儲存節點代表底層資料庫,如 MySQL、PostgreSQL、openGauss 等,計算節點則是指 ShardingSphere-JDBC 或 ShardingSphere-Proxy,並且儲存節點和計算節點的高可用方案是不同的。對於無狀態的計算節點來說,需要感知儲存節點變化的同時,還需要獨立架設負載均衡器,並具備服務發現和請求分發的能力。對於有狀態的儲存節點來說,需要其自身具備資料一致性同步、探活、主節點選舉等能力。
ShardingSphere 雖然本身並不提供資料庫的高可用能力,但可以藉助資料庫高可用能力,並通過自身資料庫發現及動態感知的能力,幫助使用者整合主從切換、故障發現、流量切換治理等一體化的資料庫高可用方案。特別是在分散式場景下搭配主從流量控制的特性,可以提供更為完善的高可用讀寫分離方案。 同時我們在使用高可用功能時,再搭配使用 DistSQL 動態調整高可用規則的動態,獲取主從的節點資訊等,使我們更能方便地運維管控 ShardingSphere 叢集。
最佳實踐
Apache ShardingSphere 採用可插拔架構,所有增量的功能能單獨使用也能相互組合。高可用功能通常和讀寫分離配合使用,在保證系統高可用的同時,配合讀寫分離將查詢請求根據負載均衡演算法分散到從庫中,減少主庫的壓力,提升業務系統的吞吐量。以下實踐內容將採用高可用 + 讀寫分離的配置,再配合使用 ShardingSphere DistSQL RAL語句進行演示。
需要注意的是,ShardingSphere 高可用的實現依賴於分散式治理的能力,所以目前只支援在叢集模式下使用。同時讀寫分離規則在 ShardingSphere 5.1.0 版本中也進行了調整,詳細內容請參考 官方文件讀寫分離。
配置參考
schemaName: database_discovery_db
dataSources:
ds_0:
url: jdbc:mysql:
//127.0.0.1:1231/demo_primary_ds?serverTimezone=UTC&useSSL=false
username: root
password:
123456
connectionTimeoutMilliseconds:
3000
idleTimeoutMilliseconds:
60000
maxLifetimeMilliseconds:
1800000
maxPoolSize:
50
minPoolSize:
1
ds_1:
url: jdbc:mysql:
//127.0.0.1:1232/demo_primary_ds?serverTimezone=UTC&useSSL=false
username: root
password:
123456
connectionTimeoutMilliseconds:
3000
idleTimeoutMilliseconds:
60000
maxLifetimeMilliseconds:
1800000
maxPoolSize:
50
minPoolSize:
1
ds_2:
url: jdbc:mysql:
//127.0.0.1:1233/demo_primary_ds?serverTimezone=UTC&useSSL=false
username: root
password:
123456
connectionTimeoutMilliseconds:
3000
idleTimeoutMilliseconds:
50000
maxLifetimeMilliseconds:
1300000
maxPoolSize:
50
minPoolSize:
1
rules:
- !READWRITE_SPLITTING
dataSources:
replication_ds:
type:
Dynamic
props:
auto-aware-data-source-
name: mgr_replication_ds
- !DB_DISCOVERY
dataSources:
mgr_replication_ds:
dataSourceNames:
- ds_0
- ds_1
- ds_2
discoveryHeartbeatName: mgr-heartbeat
discoveryTypeName: mgr
discoveryHeartbeats:
mgr-heartbeat:
props:
keep-alive-cron:
'0/5 * * * * ?'
discoveryTypes:
mgr:
type: MGR
props:
group-
name: b13df29e-
90b6-
11e8-
8d1b-
525400fc3996
前置條件
-
ShardingSphere-Proxy 5.1.0 (Cluster mode + 高可用+動態讀寫分離規則)
-
Zookeeper 3.7.0
-
MySQL MGR 叢集
SQL 指令碼
CREATE
TABLE
`t_user` (
`id`
int(
8)
NOT
NULL,
`mobile`
char(
20)
NOT
NULL,
`idcard`
varchar(
18)
NOT
NULL,
PRIMARY
KEY (
`id`)
)
ENGINE=
InnoDB
DEFAULT
CHARSET=utf8mb4;
檢視主從關係
mysql> SHOW READWRITE_SPLITTING RULES;
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
| name | auto_aware_data_source_name
| write_data_source_name | read_data_source_names
| load_balancer_type | load_balancer_props
|
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
| replication_ds
| mgr_replication_ds | ds_
0
| ds_1,ds_2 | NULL
| |
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
1 row
in set (
0.09 sec)
檢視從庫狀態
mysql> SHOW READWRITE_SPLITTING READ RESOURCES;
+----------+---------+
| resource | status
|
+----------+---------+
| ds_1
| enabled |
| ds_2 | enabled
|
+----------+---------+
從上面的結果得知,當前我們的主庫是
ds
0
,從庫是
s
1
和
ds
2
。
我們測試 INSERT 一條資料:
mysql>
INSERT INTO
t_user(
id, mobile, idcard)
value (
10000,
'13718687777',
'141121xxxxx');
Query OK,
1
row
affected (
0.10 sec)
檢視 ShardingSphere-Proxy 日誌,檢視路由的節點是否為主庫
ds_0
。
[INFO ] 2022-02-28 15:28:21.495 [ShardingSphere-Command-2] ShardingSphere-SQL - Logic SQL:
INSERT
INTO t_user(
id, mobile, idcard)
value (
10000,
'13718687777',
'141121xxxxx')
[INFO ]
2022
-02
-28
15:
28:
21.495 [ShardingSphere-Command
-2] ShardingSphere-
SQL - SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty)
[INFO ]
2022
-02
-28
15:
28:
21.495 [ShardingSphere-Command
-2] ShardingSphere-
SQL - Actual
SQL: ds_0 :::
INSERT
INTO t_user(
id, mobile, idcard)
value (
10000,
'13718687777',
'141121xxxxx')
我們測試 SELECT 一條資料(重複執行兩次):
mysql>
SELECT id, mobile, idcard
FROM t_user
WHERE id =
10000;
檢視 ShardingSphere-Proxy 日誌,檢視路由的節點是否為
ds
1
或
ds
2
。
[INFO ] 2022-02-28 15:34:07.912 [ShardingSphere-Command-4] ShardingSphere-SQL - Logic SQL:
SELECT
id, mobile, idcard
FROM t_user
WHERE
id =
10000
[INFO ]
2022
-02
-28
15:
34:
07.913 [ShardingSphere-Command
-4] ShardingSphere-
SQL - SQLStatement: MySQLSelectStatement(
table=Optional.empty,
limit=Optional.empty,
lock=Optional.empty, window=Optional.empty)
[INFO ]
2022
-02
-28
15:
34:
07.913 [ShardingSphere-Command
-4] ShardingSphere-
SQL - Actual
SQL: ds_1 :::
SELECT
id, mobile, idcard
FROM t_user
WHERE
id =
10000
[INFO ]
2022
-02
-28
15:
34:
21.501 [ShardingSphere-Command
-4] ShardingSphere-
SQL - Logic
SQL:
SELECT
id, mobile, idcard
FROM t_user
WHERE
id =
10000
[INFO ]
2022
-02
-28
15:
34:
21.502 [ShardingSphere-Command
-4] ShardingSphere-
SQL - SQLStatement: MySQLSelectStatement(
table=Optional.empty,
limit=Optional.empty,
lock=Optional.empty, window=Optional.empty)
[INFO ]
2022
-02
-28
15:
34:
21.502 [ShardingSphere-Command
-4] ShardingSphere-
SQL - Actual
SQL: ds_2 :::
SELECT
id, mobile, idcard
FROM t_user
WHERE
id =
10000
切換主庫
關閉主庫
ds_0
:
通過 DistSQL 檢視主庫是否發生改變,從庫狀態是否正確。
mysql> SHOW READWRITE_SPLITTING RULES;
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
| name | auto_aware_data_source_name
| write_data_source_name | read_data_source_names
| load_balancer_type | load_balancer_props
|
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
| replication_ds
| mgr_replication_ds | ds_1
| ds_2 | NULL
| |
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
1 row
in set (
0.
01 sec)
mysql> SHOW READWRITE_SPLITTING READ RESOURCES;
+----------+----------+
| resource | status
|
+----------+----------+
| ds_2
| enabled |
| ds_0 | disabled
|
+----------+----------+
2 rows
in set (0.01 sec)
此時我們再次 INSERT 一條資料:
mysql>
INSERT INTO
t_user(
id, mobile, idcard)
value (
10001,
'13521207777',
'110xxxxx');
Query OK,
1
row
affected (
0.04 sec)
檢視 ShardingSphere-Proxy 日誌,檢視路由的節點是否為主庫
ds_1
。
[INFO ] 2022-02-28 15:40:26.784 [ShardingSphere-Command-6] ShardingSphere-SQL - Logic SQL:
INSERT
INTO t_user(
id, mobile, idcard)
value (
10001,
'13521207777',
'110xxxxx')
[INFO ]
2022
-02
-28
15:
40:
26.784 [ShardingSphere-Command
-6] ShardingSphere-
SQL - SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty)
[INFO ]
2022
-02
-28
15:
40:
26.784 [ShardingSphere-Command
-6] ShardingSphere-
SQL - Actual
SQL: ds_1 :::
INSERT
INTO t_user(
id, mobile, idcard)
value (
10001,
'13521207777',
'110xxxxx')
最後我們測試 SELECT 一條資料(重複執行兩次):
mysql>
SELECT id, mobile, idcard
FROM t_user
WHERE id =
10001;
檢視 ShardingSphere-Proxy 日誌,檢視路由的節點是否為
ds_2
。
[INFO ] 2022-02-28 15:42:00.651 [ShardingSphere-Command-7] ShardingSphere-SQL - Logic SQL:
SELECT
id, mobile, idcard
FROM t_user
WHERE
id =
10001
[INFO ]
2022
-02
-28
15:
42:
00.651 [ShardingSphere-Command
-7] ShardingSphere-
SQL - SQLStatement: MySQLSelectStatement(
table=Optional.empty,
limit=Optional.empty,
lock=Optional.empty, window=Optional.empty)
[INFO ]
2022
-02
-28
15:
42:
00.651 [ShardingSphere-Command
-7] ShardingSphere-
SQL - Actual
SQL: ds_2 :::
SELECT
id, mobile, idcard
FROM t_user
WHERE
id =
10001
[INFO ]
2022
-02
-28
15:
42:
02.148 [ShardingSphere-Command
-7] ShardingSphere-
SQL - Logic
SQL:
SELECT
id, mobile, idcard
FROM t_user
WHERE
id =
10001
[INFO ]
2022
-02
-28
15:
42:
02.149 [ShardingSphere-Command
-7] ShardingSphere-
SQL - SQLStatement: MySQLSelectStatement(
table=Optional.empty,
limit=Optional.empty,
lock=Optional.empty, window=Optional.empty)
[INFO ]
2022
-02
-28
15:
42:
02.149 [ShardingSphere-Command
-7] ShardingSphere-
SQL - Actual
SQL: ds_2 :::
SELECT
id, mobile, idcard
FROM t_user
WHERE
id =
10001
上線從庫
通過 DistSQL 檢視最新的主從關係是否變化, ds 0 節點狀態恢復為啟用狀態, ds 0 加入 read data source_names:
mysql> SHOW READWRITE_SPLITTING RULES;
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
| name | auto_aware_data_source_name
| write_data_source_name | read_data_source_names
| load_balancer_type | load_balancer_props
|
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
| replication_ds
| mgr_replication_ds | ds_1
| ds_0,ds_2 | NULL
| |
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
1 row
in set (
0.
01 sec)
mysql> SHOW READWRITE_SPLITTING READ RESOURCES;
+----------+---------+
| resource | status
|
+----------+---------+
| ds_
0
| enabled |
| ds_2 | enabled
|
+----------+---------+
2 rows
in set (0.00 sec)
通過上面的實踐,相信大家已經對 ShardingSphere 高可用和動態讀寫分離有了一定的認識。接下來為大家介紹基於底層資料庫的儲存節點的高可用方案實現原理。
實現原理
ShardingSphere 提供了高可用方案,允許使用者進行二次定製開發及實現擴充套件。目前實現了基於 MGR 的 MySQL 高可用方案以及由社群同學開發貢獻的 openGauss 資料庫高可用方案。兩種方案的實現思路整體一致,下文將以 MySQL 為例,詳細介紹 ShardingSphere 實現資料庫高可用的底層原理以及最佳實踐。
前置檢查
ShardingSphere 通過執行以下 SQL 驗證底層 MySQL 叢集環境是否準備完成,未滿足其中任意一個檢查 ShardingSphere 均無法正常啟動。
- 檢查是否安裝了 MGR 外掛;
SELECT *
FROM information_schema.PLUGINS
WHERE PLUGIN_NAME=
'group_replication'
-
檢視 MGR 組成員數量;
- 底層 MGR 叢集最低要求是由三個節點組成;
SELECT
count(*)
FROM performance_schema.replication_group_members
-
校驗 MGR 叢集的 group name 是否與配置中的 group name 一致;
- group name 是 MGR 組的標識,一組 MGR 叢集對應同一個 group name;
SELECT *
FROM performance_schema.global_variables
WHERE VARIABLE_NAME=
'group_replication_group_name'
-
檢視當前 MGR 是否設定為單主模式;
- ShardingSphere 不支援雙寫或多寫場景,只能是單寫模式;
SELECT *
FROM performance_schema.global_variables
WHERE VARIABLE_NAME=
'group_replication_single_primary_mode'
- 查詢 MGR 組叢集中所有的節點地址,埠及狀態。用於校驗我們配置的資料來源是否正確;
SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE
FROM performance_schema.replication_group_members
動態發現主庫
- ShardingSphere 根據 MySQL 提供的查詢主庫 SQL 命令獲取主庫 URL。
private
String findPrimaryDataSourceURL(
final
Map<
String, DataSource> dataSourceMap) {
String result =
"";
String sql =
"SELECT MEMBER_HOST, MEMBER_PORT FROM performance_schema.replication_group_members WHERE MEMBER_ID = "
+
"(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'group_replication_primary_member')";
for (DataSource each : dataSourceMap.values()) {
try (Connection connection = each.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql)) {
if (resultSet.next()) {
return
String.format(
"%s:%s", resultSet.getString(
"MEMBER_HOST"), resultSet.getString(
"MEMBER_PORT"));
}
}
catch (
final SQLException ex) {
log.error(
"An exception occurred while find primary data source url", ex);
}
}
return result;
}
- 上面查詢出來的主庫 URL 與我們配置的 dataSources URL 逐一對比。命中的資料來源則為主庫,將主庫更新至 ShardingSphere 當前記憶體中並持久化至註冊中心,通過註冊中心分發至叢集內其它計算節點中。
動態發現從庫
ShardingSphere 從庫狀態分為啟動與禁用,並且從庫的狀態實時地同步至 ShardingSphere 記憶體中,以保證讀流量可以被正確的路由。
- 獲取 MGR 組中所有的節點
SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE
FROM performance_schema.replication_group_members
- 禁用從庫
private
void determineDisabledDataSource(
final
String schemaName,
final
Map<
String, DataSource> activeDataSourceMap,
final
List<
String> memberDataSourceURLs,
final
Map<
String,
String> dataSourceURLs) {
for (Entry<
String, DataSource> entry : activeDataSourceMap.entrySet()) {
boolean disable =
true;
String url =
null;
try (Connection connection = entry.getValue().getConnection()) {
url = connection.getMetaData().getURL();
for (
String each : memberDataSourceURLs) {
if (
null != url && url.contains(each)) {
disable =
false;
break;
}
}
}
catch (
final SQLException ex) {
log.error(
"An exception occurred while find data source urls", ex);
}
if (disable) {
ShardingSphereEventBus.getInstance().post(
new DataSourceDisabledEvent(schemaName, entry.getKey(),
true));
}
else
if (!url.isEmpty()) {
dataSourceURLs.put(entry.getKey(), url);
}
}
}
從庫的禁用,依據的是我們配置的資料來源以及 MGR 組中所有的節點。ShardingSphere 會逐一檢查我們配置的資料來源是否可以正常的獲取 Connection,並校驗資料來源 URL 是否包含 MGR 組中的節點。無法正常獲取 Connection 或校驗失敗,ShardingSphere 會事件驅動禁用資料來源,以及同步註冊中心。
圖片
- 啟用從庫
private
void determineEnabledDataSource(
final
Map<
String, DataSource> dataSourceMap,
final
String schemaName,
final
List<
String> memberDataSourceURLs,
final
Map<
String,
String> dataSourceURLs) {
for (
String each : memberDataSourceURLs) {
boolean enable =
true;
for (Entry<
String,
String> entry : dataSourceURLs.entrySet()) {
if (entry.getValue().contains(each)) {
enable =
false;
break;
}
}
if (!enable) {
continue;
}
for (Entry<
String, DataSource> entry : dataSourceMap.entrySet()) {
String url;
try (Connection connection = entry.getValue().getConnection()) {
url = connection.getMetaData().getURL();
if (
null != url && url.contains(each)) {
ShardingSphereEventBus.getInstance().post(
new DataSourceDisabledEvent(schemaName, entry.getKey(),
false));
break;
}
}
catch (
final SQLException ex) {
log.error(
"An exception occurred while find enable data source urls", ex);
}
}
}
}
恢復當機從庫,重新加入 MGR 組後,檢查我們配置中是否使用了被恢復的資料來源。如果使用則會事件驅動告訴 ShardingSphere 需要該資料來源恢復成啟用狀態。
心跳檢測機制
上面帶大家瞭解了 ShardingSphere 動態發現和更新主從資料庫狀態的詳細流程,同時,為了保證主從狀態同步的實時性,高可用模組引入心跳檢測機制,通過整合 ShardingSphere 子專案 ElasticJob,在高可用模組初始化時將上述的流程以 Job 的方式交由 ElasticJob 排程框架執行,實現了功能開發和作業排程的分離,開發者如果需要擴充套件高可用的功能,也無需關心作業如何開發執行的問題。
private
void initHeartBeatJobs(
final
String schemaName,
final
Map<
String, DataSource> dataSourceMap) {
Optional<ModeScheduleContext> modeScheduleContext = ModeScheduleContextFactory.getInstance().
get();
if (modeScheduleContext.isPresent()) {
for (Entry<
String, DatabaseDiscoveryDataSourceRule> entry : dataSourceRules.entrySet()) {
Map<
String, DataSource> dataSources = dataSourceMap.entrySet().stream().filter(dataSource -> !entry.getValue().getDisabledDataSourceNames().contains(dataSource.getKey()))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
CronJob job =
new CronJob(entry.getValue().getDatabaseDiscoveryType().getType() +
"-" + entry.getValue().getGroupName(),
each ->
new HeartbeatJob(schemaName, dataSources, entry.getValue().getGroupName(), entry.getValue().getDatabaseDiscoveryType(), entry.getValue().getDisabledDataSourceNames())
.execute(
null), entry.getValue().getHeartbeatProps().getProperty(
"keep-alive-cron"));
modeScheduleContext.
get().startCronJob(job);
}
}
}
結語
Apache ShardingSphere 高可用目前支援 MySQL 和 openGauss 高可用方案,未來將整合更多 MySQL 高可用產品以及支援更多其它資料庫高可用方案,歡迎大家一起參與。同時 ShardingSphere 高可用也是 SphereEx 中文社群 Governance 興趣小組長期維護的模組之一,對分散式治理和高可用感興趣的同學,也請關注 SphereEx 中文社群並加入我們的興趣小組。
GitHub issue:
貢獻指南:
中文社群興趣小組:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70001955/viewspace-2867631/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 基於MFS高可用的分散式儲存架構分散式架構
- Apache ShardingSphere:由開源驅動的分散式資料庫中介軟體生態Apache分散式資料庫
- 基於Apache Hudi + MinIO 構建流式資料湖Apache
- Apache ShardingSphere 如何實現分散式事務Apache分散式
- LVS + Keepalived + Nginx基於DR模式構建高可用方案Nginx模式
- DistSQL:像資料庫一樣使用 Apache ShardingSphereSQL資料庫Apache
- Apache HBase 1.7.1 釋出,分散式資料庫Apache分散式資料庫
- Uber基於Apache Hudi構建PB級資料湖實踐Apache
- 基於Apache Hudi在Google雲構建資料湖平臺ApacheGo
- 基於 EventBridge 構建資料庫應用整合資料庫
- MySQL資料庫架構——高可用演進MySql資料庫架構
- 分散式及高可用後設資料採集原理分散式
- 基於Centos7.x平臺搭建PhxSQL+Atlas+MySQL高可用資料庫架構CentOSMySql資料庫架構
- 構建基於RocketMQ的分散式事務服務MQ分散式
- HarmonyOS Next方舟資料管理與分散式資料庫實戰:構建高效同步架構分散式資料庫架構
- 基於Amazon ECS Fargate構建Apache SupersetApache
- 基於Redis的低成本高可用排行榜服務構建Redis
- SpringBoot基於資料庫實現簡單的分散式鎖Spring Boot資料庫分散式
- 打造基於 PostgreSQL/openGauss 的分散式資料庫解決方案SQL分散式資料庫
- 基於資料庫、redis和zookeeper實現的分散式鎖資料庫Redis分散式
- 基於SpringCloud分散式架構SpringGCCloud分散式架構
- OB有問必答 | 分散式資料庫有哪些常用的高可用及容災方案?分散式資料庫
- MySQL資料庫高可用方案MySql資料庫
- ES資料庫高可用配置資料庫
- 基於Apache Hudi構建資料湖的典型應用場景介紹Apache
- 分散式資料庫架構原理 - Alex Petrov分散式資料庫架構
- 分散式資料庫分散式資料庫
- (二) MdbCluster分散式記憶體資料庫——分散式架構1分散式記憶體資料庫架構
- 多利熊基於分散式架構實踐穩定性建設分散式架構
- 基於 dubbo 的分散式架構分散式架構
- 整合spring cloud雲架構 -高可用的分散式配置中心SpringCloud架構分散式
- Apache ShardingSphere 後設資料載入剖析Apache
- posgresql資料庫高可用方案-patroniSQL資料庫
- (一) MdbCluster分散式記憶體資料庫——基礎架構介紹分散式記憶體資料庫架構
- 分散式資料庫的架構演變之路分散式資料庫架構
- 崑崙分散式資料庫架構介紹分散式資料庫架構
- 構建基於 HarmonyOS Next 的分散式工業監控系統分散式
- ShardingSphere x Seata,一致性更強的分散式資料庫中介軟體分散式資料庫