Mi az az adatbázis magas rendelkezésre állása (HA)?
A digitális korban a modern üzleti rendszereknek rendkívül elérhetőnek, megbízhatónak és stabilnak kell lenniük. A modern üzleti rendszer sarokköveként az adatbázisoknak fel kell ölelniük a magas rendelkezésre állást.
A HA lehetővé teszi az adatbázisok számára, hogy szolgáltatásokat váltsanak az elsődleges és másodlagos adatbázisok között, és automatikusan kiválasszák a Master
értéket, így kiválaszthatja a legjobb csomópontot főként, amikor az előző összeomlik.
MySQL magas rendelkezésre állás
Rengeteg „MySQL magas rendelkezésre állás” opció létezik, de mindegyiknek megvannak a maga előnyei és hátrányai. Az alábbiakban bemutatunk néhány általánosan elterjedt magas rendelkezésre állási lehetőséget:
- Az Orchestrator egy MySQL HA- és replikációs topológia-kezelő eszköz, amely Go nyelven íródott. Előnye abban rejlik, hogy támogatja az elsődleges-másodlagos topológia kézi beállítását, az automatikus feladatátvételt, a fő csomópontok automatikus vagy kézi helyreállítását a webes vizuális konzolon keresztül. A programot azonban külön kell telepíteni, és a bonyolult konfigurációk miatt meredek tanulási görbéje is van.
- Az „MHA” egy másik kiforrott megoldás. Elsődleges/másodlagos kapcsolási és feladatátvételi képességeket biztosít. Az a jó benne, hogy a legkevesebb adatvesztést tudja biztosítani a váltási folyamat során, miközben képes félszinkron és aszinkron replikációs keretrendszerekkel dolgozni. Az MHA indulása után azonban csak a
Master
figyelhető meg, és az MHA nem biztosítja a terheléselosztásiszolgáltatást az olvasott adatbázishoz. - Az MGR az elosztott Paxos protokollon alapuló csoportreplikációt valósít meg az adatok konzisztenciájának biztosítása érdekében. Ez a MySQL által biztosított hivatalos HA összetevő, és nincs szükség további telepítési programra. Ehelyett a felhasználóknak csak az MGR beépülő modult kell telepíteniük az egyes adatforrás-csomópontokra. Az eszköz nagy konzisztenciával, nagy hibatűréssel, nagy skálázhatósággal és nagy rugalmassággal rendelkezik.
Apache ShardingSphere magas rendelkezésre állás
Az Apache ShardingSphere architektúrája valójában elválasztja a tárolást a számítástechnikától. A tárolási csomópont az alapul szolgáló adatbázist képviseli, mint például a MySQL, PostgreSQL, openGauss stb., míg a számítási csomópont a ShardingSphere-JDBC vagy ShardingSphere-Proxy névre utal.
Ennek megfelelően a tárolási csomópontok és a számítási csomópontok „magas rendelkezésre állású megoldásai” eltérőek. Az állapot nélküli számítási csomópontok esetében, miközben érzékelniük kell a tárolócsomópontokban bekövetkezett változásokat, külön terheléselosztókat is be kell állítaniuk, és rendelkezniük kell a szolgáltatásfelderítési és kéréselosztási képességekkel. Az állapottartó tároló csomópontokat fel kell szerelni az adatszinkronizálás, a kapcsolatteszt, a főcsomópont kiválasztásának stb. lehetőségeivel.
Bár a ShardingSphere nem biztosít magas rendelkezésre állású adatbázist, segíthet a felhasználóknak az adatbázis-HA-megoldások integrálásában, mint például az elsődleges-másodlagos átkapcsolás, a hibafelderítés, a forgalomváltás irányítása stb. felfedezés és dinamikus észlelés.
Az elosztott forgatókönyvekben az elsődleges-másodlagos áramlásvezérlő funkcióval kombinálva a ShardingSphere jobb magas rendelkezésre állású olvasási/írási felosztási megoldásokat kínál. Könnyebb lesz a ShardingSphere-fürtök működtetése és kezelése, ha a DistSQL dinamikus magas rendelkezésre állási beállítási szabályait használjuk az elsődleges/másodlagos csomópontok információinak megszerzésére.
Legjobb gyakorlatok
Az Apache ShardingSphere beépülő modul-orientált architektúrát alkalmaz, így minden továbbfejlesztett képessége önállóan vagy együtt is használható. Magas rendelkezésre állású funkcióját gyakran használják az olvasási/írási felosztással együtt a lekérdezési kérések elosztására a szolga adatbázisok között a terheléselosztási algoritmus szerint, így biztosítva a rendszer HA-t, enyhítve az elsődleges adatbázisok nyomását, és javítva az üzleti rendszer átviteli sebességét.
Itt példának vesszük a HA+olvasási/írási felosztási konfigurációt ShardingSphere DistSQL RAL utasításokkal.
Egy dologra fel kell hívni a figyelmet, hogy a ShardingSphere HA megvalósítás az elosztott irányítási képességére támaszkodik. Ezért egyelőre csak fürt módban használható. Eközben a ShardingSphere 5.1.0 átdolgozza az olvasási/írási felosztási szabályokat. A részletekért tekintse meg a hivatalos dokumentációt az írás/olvasás felosztásáról.
Konfiguráció
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
Követelmények
- ShardingSphere-Proxy 5.1.0 (fürt mód + HA + dinamikus olvasási/írási felosztási szabály)
- Zookeeper 3.7.0
- MySQL MGR-fürt
SQL-szkript
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;
Tekintse meg az elsődleges-másodlagos kapcsolatot
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)
Tekintse meg a másodlagos adatbázis állapotát
mysql> SHOW READWRITE_SPLITTING READ RESOURCES; +----------+---------+ | resource | status | +----------+---------+ | ds_1 | enabled | | ds_2 | enabled | +----------+---------+
A fenti eredményekből megtudhatjuk, hogy jelenleg az elsődleges adatbázis az ds_0
, míg a másodlagos adatbázisok az ds_1
és ds_2
.
Teszteljük INSERT
:
mysql> INSERT INTO t_user(id, mobile, idcard) value (10000, '13718687777', '141121xxxxx');Query OK, 1 row affected (0.10 sec)
Tekintse meg a ShardingSphere-Proxy naplót, és ellenőrizze, hogy az útvonal csomópontja-e az elsődleges adatbázis 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')
Teszteljük SELECT
(ismételjük meg kétszer):
mysql> SELECT id, mobile, idcard FROM t_user WHERE id = 10000;
Tekintse meg a ShardingSphere-Proxy naplót, és nézze meg, hogy az útvonal csomópontja ds_1
vagy 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
Váltson az elsődleges adatbázisra
Zárja be a fő adatbázist ds_0
:
Tekintse meg, hogy az elsődleges adatbázis megváltozott-e, és hogy a másodlagos adatbázis állapota helyes-e a DistSQL
-ig.
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)
Most pedig illesszünk be egy másik adatsort:
mysql> INSERT INTO t_user(id, mobile, idcard) value (10001, '13521207777', '110xxxxx');Query OK, 1 row affected (0.04 sec)
Tekintse meg a ShardingSphere-Proxy naplót, és ellenőrizze, hogy az útvonal csomópontja-e az elsődleges adatbázis 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')
Végül teszteljük a SELECT
-t (ismételjük meg kétszer):
mysql> SELECT id, mobile, idcard FROM t_user WHERE id = 10001;
Tekintse meg a ShardingSphere-Proxy naplót, és nézze meg, hogy az útvonal csomópontja ds_2
.
[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')
Engedje el a másodlagos adatbázisokat
Tekintse meg a legújabb elsődleges-másodlagos kapcsolatváltozásokat a DistSQL
-ig. A ds_0
csomópont állapota az engedélyezett állapot szerint helyreáll, míg a ds_0
integrálva van a read_data_source_names
-ba:
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)
A fent említett példa alapján most többet tud a ShardingSphere magas rendelkezésre állásáról és a dinamikus olvasási/írási felosztásról.
Ezután bemutatjuk a HA-tervek mögött meghúzódó elveket az alapul szolgáló adatbázissal, más néven tárolási csomópontokkal kapcsolatban.
Alapelvek
A ShardingSphere magas rendelkezésre állású megoldásai lehetővé teszik a felhasználók számára, hogy tovább szabják, és bővítményeket készítsenek. Jelenleg két HA tervet teljesítettünk: egy MySQL magas rendelkezésre állású MGR-re épülő megoldást, és az openGauss adatbázis magas rendelkezésre állású megoldását, amelyet néhány közösségi elkötelezettség támogat. A két megoldás elve alapvetően megegyezik.
Az alábbiakban bemutatjuk, miért és hogyan érheti el a ShardingSphere az adatbázis magas szintű rendelkezésre állását példaként a MySQL használatával.
Előfeltétel
A ShardingSphere a következő SQL utasítás végrehajtásával ellenőrzi, hogy az alapul szolgáló MySQL-fürtkörnyezet készen áll-e. A ShardingSphere nem indítható el, ha bármelyik teszt sikertelen.
- Ellenőrizze, hogy az MGR telepítve van-e:
SELECT * FROM information_schema.PLUGINS WHERE PLUGIN_NAME='group_replication'
- Tekintse meg az MGR csoport tagszámát:
Az alapul szolgáló MGR-fürtnek legalább három csomópontból kell állnia:
SELECT count(*) FROM performance_schema.replication_group_members
- Ellenőrizze, hogy az MGR-fürt csoportneve összhangban van-e a konfigurációban szereplővel:
A csoportnév egy MGR-csoport jelölője, és az MGR-fürt minden csoportjának csak egy csoportneve van.
SELECT * FROM performance_schema.global_variables WHERE VARIABLE_NAME='group_replication_group_name'
- Ellenőrizze, hogy az aktuális MGR van-e beállítva egyetlen elsődleges módként:
Jelenleg a ShardingSphere nem támogatja a kettős vagy több írásos forgatókönyveket. Csak az egyszeri írási módot támogatja:
SELECT * FROM performance_schema.global_variables WHERE VARIABLE_NAME='group_replication_single_primary_mode'
- Az MGR-csoportfürt összes csomóponti gazdagépének, portjának és állapotának lekérdezésével ellenőrizze, hogy az általunk konfigurált adatforrás helyes-e:
SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE FROM performance_schema.replication_group_members
Dinamikus elsődleges adatbázis-felderítés
- A ShardingSphere megkeresi az elsődleges adatbázis URL-címét a MySQL által biztosított lekérdezési főadatbázis SQL-parancsnak megfelelően.
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; }
- Hasonlítsa össze egyenként a fent talált elsődleges adatbázis URL-eket az általunk konfigurált
dataSources
URL-ekkel. Az egyeztetett adatforrás az elsődleges adatbázis, amely frissül az aktuális ShardingSphere memóriára, és állandósul a regisztrációs központban, amelyen keresztül a fürt más számítási csomópontjaihoz továbbítják.
Dinamikus másodlagos adatbázis-felderítés
A ShardingSpherezben kétféle másodlagos adatbázisállapot létezik: engedélyezés és letiltás. A másodlagos adatbázis állapota szinkronizálva lesz a ShardingSphere memóriával annak érdekében, hogy az olvasási forgalom megfelelően irányítható legyen.
- Szerezze be az MGR csoport összes csomópontját:
SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE FROM performance_schema.replication_group_members
- Másodlagos adatbázisok letiltása:
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); } } }
Az, hogy a másodlagos adatbázis le van-e tiltva, az általunk konfigurált adatforráson és az MGR-csoport összes csomópontján alapul.
A ShardingSphere egyesével ellenőrizheti, hogy az általunk konfigurált adatforrás megfelelően be tudja-e szerezni a Connection
értéket, és ellenőrizheti, hogy az adatforrás URL-je tartalmazza-e az MGR-csoport csomópontjait.
Ha a Connection
nem érhető el, vagy az ellenőrzés sikertelen, a ShardingSphere letiltja az adatforrást egy eseményindítóval, és szinkronizálja azt a regisztrációs központtal.
- Másodlagos adatbázisok engedélyezése:
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); } } }
Miután az összeomlott másodlagos adatbázist helyreállították és hozzáadták az MGR-csoporthoz, a konfigurációnk ellenőrzi, hogy a helyreállított adatforrást használják-e. Ha igen, az eseményindító jelzi a ShardingSphere-nek, hogy az adatforrást engedélyezni kell.
Szívverés mechanizmusa
Az elsődleges-másodlagos állapotok valós idejű szinkronizálása érdekében a szívverés mechanizmusa bekerül a HA modulba.
Az ElasticJob ShardingSphere alprojekt integrálásával a fenti folyamatokat az ElasticJob ütemező keretrendszer Job formájában hajtja végre, amikor a HA modul inicializálva van, így valósul meg a funkciófejlesztés és a feladatütemezés szétválasztása.
Még ha a fejlesztőknek ki kell terjeszteniük a HA funkciót, nem kell törődniük azzal, hogy a munkákat hogyan alakítják ki és működtetik.
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); } } }
Következtetés
Eddig az Apache ShardingSphere HA funkciója alkalmazhatónak bizonyult a MySQL és az openGauss HA megoldásokhoz.
A jövőben több MySQL HA-terméket fog integrálni, és több adatbázis-HA-megoldást támogat.
Mint mindig, ha felkeltettük érdeklődését, örömmel csatlakozhat hozzánk, és hozzájárulhat az Apache ShardingSphere projekthez.
Apache ShardingSphere nyílt forráskódú projekthivatkozások:
"ShardingSphere Github"
"ShardingSphere Twitter"
"ShardingSphere Slack"
„Közreműködői irányelv”
Szerző
Zhao Jinchao
SphereEx Middleware mérnök és Apache ShardingSphere Committer
Jelenleg Zhao az Apache ShardingSphere High Availability szolgáltatásának fejlesztésére koncentrál.