Eine kurze Diskussion zur Auftragsrekonstruktion: MySQL-Sharding

Eine kurze Diskussion zur Auftragsrekonstruktion: MySQL-Sharding

1. Ziele

Mit diesem Artikel werden die folgenden Ziele erreicht:

  • Anzahl der Untertabellen: 256 Anzahl der Unterdatenbanken: 4
  • Benutzer-ID (user_id) als Datenbank-Sharding-Schlüssel verwenden
  • Testen Sie abschließend die Vorgänge zur Auftragserstellung, -aktualisierung und -löschung, zur Abfrage einzelner Auftragsnummern und zur Listenabfrage basierend auf der Benutzer-ID.

Architekturdiagramm:

Die Tabellenstruktur ist wie folgt:

CREATE TABLE `order_XXX` (
  `order_id` bigint(20) unsigned NICHT NULL,
  `user_id` int(11) DEFAULT '0' COMMENT 'Bestell-ID',
  `status` int(11) DEFAULT '0' COMMENT 'Bestellstatus',
  `booking_date` Datum/Uhrzeit DEFAULT NULL,
  `create_time` Datum/Uhrzeit DEFAULT NULL,
  `update_time` Datum/Uhrzeit DEFAULT NULL,
  PRIMÄRSCHLÜSSEL (`order_id`),
  SCHLÜSSEL `idx_user_id` (`Benutzer-ID`),
  SCHLÜSSEL `idx_bdate` (`Buchungsdatum`),
  SCHLÜSSEL `idx_ctime` (`Erstellungszeit`),
  SCHLÜSSEL `idx_utime` (`update_time`)
)ENGINE=InnoDB STANDARD-CHARSET=utf8;

Hinweis: 000 <= XXX <= 255. Dieser Artikel konzentriert sich auf die Praxis des Shardings von Datenbanken und Tabellen. Nur repräsentative Felder werden beibehalten. Andere Szenarien können auf dieser Grundlage verbessert werden.

Weltweit einzigartiges ID-Design

Voraussetzungen: 1. Weltweit eindeutig 2: Grob geordnet 3: Umkehrbare ausgehende Nummer

  • 1 Bit + 39 Bit Zeitdifferenz + 8 Bit Maschinennummer + 8 Bit Benutzernummer (Bibliotheksnummer) + 8 Bit Auto-Inkrement-Sequenz

Bestellnummer Komponenten Reservierte Felder Zeitunterschied im Millisekundenbereich Anzahl der Maschinen Benutzer-ID (Tabellen-ID) Auto-Inkrement-Sequenz
Belegte Bytes (Einheit: Bit) 1 39 8 8 8

Maximale QPS einer einzelnen Maschine: 256.000 Lebensdauer: 17 Jahre

2. Umweltvorbereitung

1. Grundlegende Informationen

Artikel Version Bemerkung
SpringBoot 2.1.10.FREIGABE
Mango 1.6.16 Wiki-Adresse: https://github.com/jfaster/mango
Hikari CP 3.2.0
MySQL 5.7 Testen mit Docker One-Click Build

2. Vorbereitung der Datenbankumgebung

Geben Sie mysql ein:

#Hauptdatenbank mysql -h 172.30.1.21 -uroot -pbytearch

#Aus der Bibliothek mysql -h 172.30.1.31 -uroot -pbytearch


Betreten des Containers

# Haupt-Docker exec -it db_1_master /bin/bash

#Von Docker exec -it db_1_slave /bin/bash


Überprüfen des Ausführungsstatus

#Haupt-Docker exec db_1_master sh -c 'mysql -u root -pbytearch -e "SHOW MASTER STATUS \G"'
#Von Docker exec db_1_slave sh -c 'mysql -u root -pbytearch -e "SHOW SLAVE STATUS \G"'

3. Datenbank erstellen und Untertabellen importieren

(1) Erstellen Sie Datenbanken in der MySQL-Masterinstanz

172.30.1.21(Auftragsdatenbank_1), 172.30.1.22(Auftragsdatenbank_2),

172.30.1.23(Bestelldatenbank_3) , 172.30.1.24(Bestelldatenbank_4)

(2) Importieren Sie die SQL-Befehle zum Erstellen der Tabellen nacheinander:

mysql -uroot -pbytearch -h172.30.1.21 order_db_1<fast-cloud-mysql-sharding/doc/sql/order_db_1.sql;
mysql -uroot -pbytearch -h172.30.1.22 order_db_2<fast-cloud-mysql-sharding/doc/sql/order_db_2.sql;
mysql -uroot -pbytearch -h172.30.1.23 order_db_3<fast-cloud-mysql-sharding/doc/sql/order_db_3.sql;
mysql -uroot -pbytearch -h172.30.1.24 order_db_4<fast-cloud-mysql-sharding/doc/sql/order_db_4.sql;  

3. Konfiguration und Praxis

1. POM-Datei

     <!-- Mango-Datenbank- und Tabellen-Sharding-Middleware --> 
            <Abhängigkeit>
                <groupId>org.jfaster</groupId>
                <artifactId>Mango-Spring-Boot-Starter</artifactId>
                <version>2.0.1</version>
            </Abhängigkeit>
         
             <!-- Verteilter ID-Generator -->
            <Abhängigkeit>
                <groupId>com.bysearch</groupId>
                <artifactId>schneller Cloud-ID-Generator</artifactId>
                <version>${version}</version>
            </Abhängigkeit>

            <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
            <Abhängigkeit>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>6.0.6</version>
            </Abhängigkeit>

2. Konstante Konfiguration

Paket com.bytearch.fast.cloud.mysql.sharding.common;

/**
 * Gemeinsame Konstanten für Datenbank- und Tabellen-Sharding-Strategien*/
öffentliche Klasse ShardingStrategyConstant {
    /**
     * logischer Datenbankname, der tatsächliche Datenbankname ist order_db_XXX
     */
    öffentliche statische endgültige Zeichenfolge LOGIC_ORDER_DATABASE_NAME = "order_db";
    /**
     * Die Anzahl der Untertabellen beträgt 256. Nach der Bestätigung kann sie nicht mehr geändert werden.*/
    öffentliche statische endgültige Int SHARDING_TABLE_NUM = 256;

    /**
     * Es wird nicht empfohlen, die Anzahl der Unterdatenbanken zu ändern. Sie kann geändert werden, aber der DBA muss die Daten migrieren*/
    öffentliche statische endgültige Int SHARDING_DATABASE_NODE_NUM = 4;
}

3. YML-Konfiguration

4 Master- und 4 Slave-Datenbankkonfigurationen. Hier testen wir nur das Standardkennwort des Root-Benutzers. Es wird nicht empfohlen, den Root-Benutzer in einer Produktionsumgebung zu verwenden.

Mango:
  Scan-Paket: com.bytearch.fast.cloud.mysql.sharding.dao
  Datenquellen:
    - Name: order_db_1
      Master:
        Treiberklassenname: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://172.30.1.21:3306/order_db_1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
        Benutzername: root
        Passwort: bytearch
        maximale Poolgröße: 10
        Verbindungs-Timeout: 3000
      Sklaven:
        - Treiberklassenname: com.mysql.cj.jdbc.Driver
          jdbc-URL: jdbc:mysql://172.30.1.31:3306/order_db_1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
          Benutzername: root
          Passwort: bytearch
          maximale Poolgröße: 10
          Verbindungs-Timeout: 3000
    - Name: order_db_2
      Master:
        Treiberklassenname: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://172.30.1.22:3306/order_db_2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
        Benutzername: root
        Passwort: bytearch
        maximale Poolgröße: 10
        Verbindungs-Timeout: 3000
      Sklaven:
        - Treiberklassenname: com.mysql.cj.jdbc.Driver
          jdbc-URL: jdbc:mysql://172.30.1.32:3306/order_db_2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
          Benutzername: root
          Passwort: bytearch
          maximale Poolgröße: 10
          Verbindungs-Timeout: 3000
    - Name: order_db_3
      Master:
        Treiberklassenname: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://172.30.1.23:3306/order_db_3?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
        Benutzername: root
        Passwort: bytearch
        maximale Poolgröße: 10
        Verbindungs-Timeout: 3000
      Sklaven:
        - Treiberklassenname: com.mysql.cj.jdbc.Driver
          jdbc-url: jdbc:mysql://172.30.1.33:3306/order_db_3?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
          Benutzername: root
          Passwort: bytearch
          maximale Poolgröße: 10
          Verbindungs-Timeout: 3000
    - Name: order_db_4
      Master:
        Treiberklassenname: com.mysql.cj.jdbc.Driver
        jdbc-URL: jdbc:mysql://172.30.1.24:3306/order_db_4?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
        Benutzername: root
        Passwort: bytearch
        maximale Poolgröße: 10
        Verbindungs-Timeout: 3000
      Sklaven:
        - Treiberklassenname: com.mysql.cj.jdbc.Driver
          jdbc-url: jdbc:mysql://172.30.1.34:3306/order_db_4?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false
          Benutzername: root
          Passwort: bytearch
          maximale Poolgröße: 10
          Verbindungs-Timeout: 300

4. Strategie zum Sharding von Datenbanken und Tabellen

1). Verwenden Sie order_id als ShardKey, um Datenbank und Tabelle zu trennen

Paket com.bytearch.fast.cloud.mysql.sharding.strategy;

importiere com.bytearch.fast.cloud.mysql.sharding.common.ShardingStrategyConstant;
importiere com.bytearch.id.generator.IdEntity;
importiere com.bytearch.id.generator.SeqIdUtil;
importiere org.jfaster.mango.sharding.ShardingStrategy;

/**
 * Bestellnummer Unterbibliothek und Untertabelle Strategie */
öffentliche Klasse OrderIdShardingStrategy implementiert ShardingStrategy<Long, Long> {
    @Überschreiben
    öffentliche Zeichenfolge getDataSourceFactoryName(Lange Bestell-ID) {
        wenn (Bestell-ID == null || Bestell-ID < 0L) {
            throw new IllegalArgumentException("order_id ist ungültig!");
        }
        idEntity idEntity = SeqIdUtil.decodeId(orderId);
        wenn (idEntity.getExtraId() >= ShardingStrategyConstant.SHARDING_TABLE_NUM) {
            throw new IllegalArgumentException("Sharding-Tabelle Num ist ungültig, tableNum:" + idEntity.getExtraId());
        }
        //1. Schrittlänge berechnen int step = ShardingStrategyConstant.SHARDING_TABLE_NUM / ShardingStrategyConstant.SHARDING_DATABASE_NODE_NUM;
        //2. Berechnen Sie die Bibliotheksnummer long dbNo = Math.floorDiv(idEntity.getExtraId(), step) + 1;
        //3. Gibt den Datenquellennamen zurück return String.format("%s_%s", ShardingStrategyConstant.LOGIC_ORDER_DATABASE_NAME, dbNo);
    }

    @Überschreiben
    public String getTargetTable(String logicTableName, Lange Bestell-ID) {
        wenn (Bestell-ID == null || Bestell-ID < 0L) {
            throw new IllegalArgumentException("order_id ist ungültig!");
        }
        idEntity idEntity = SeqIdUtil.decodeId(orderId);
        wenn (idEntity.getExtraId() >= ShardingStrategyConstant.SHARDING_TABLE_NUM) {
            throw new IllegalArgumentException("Sharding-Tabelle Num ist ungültig, tableNum:" + idEntity.getExtraId());
        }
        // Der tatsächliche Tabellenname lautet gemäß der Konvention logicTableName_XXX. Wenn XXX weniger als drei Ziffern hat, fügen Sie 0 hinzu.
        return String.format("%s_%03d", logicTableName, idEntity.getExtraId());
    }
}

2). Verwenden Sie user_id als ShardKey, um die Datenbank und die Tabelle zu sharden

Paket com.bytearch.fast.cloud.mysql.sharding.strategy;

importiere com.bytearch.fast.cloud.mysql.sharding.common.ShardingStrategyConstant;
importiere org.jfaster.mango.sharding.ShardingStrategy;

/**
 *Geben Sie den Sharding-Schlüssel und die Sharding-Strategie für Datenbank/Tabelle an*/
öffentliche Klasse UserIdShardingStrategy implementiert ShardingStrategy<Integer, Integer> {

    @Überschreiben
    public String getDataSourceFactoryName(Integer Benutzer-ID) {
        //1. Berechnen Sie die Schrittlänge, d. h. die Anzahl der Tabellen in einer einzelnen Datenbank int step = ShardingStrategyConstant.SHARDING_TABLE_NUM / ShardingStrategyConstant.SHARDING_DATABASE_NODE_NUM;
        //2. Berechnen Sie die Datenbanknummer long dbNo = Math.floorDiv(userId % ShardingStrategyConstant.SHARDING_TABLE_NUM, step) + 1;
        //3. Gibt den Datenquellennamen zurück return String.format("%s_%s", ShardingStrategyConstant.LOGIC_ORDER_DATABASE_NAME, dbNo);
    }

    @Überschreiben
    public String getTargetTable(String logicTableName, Integer userId) {
        // Der tatsächliche Tabellenname lautet gemäß Konvention logicTableName_XXX. Wenn XXX weniger als drei Ziffern hat, addieren Sie 0.
        return String.format("%s_%03d", Logiktabellenname, Benutzer-ID % ShardingStrategyConstant.SHARDING_TABLE_NUM);
    }
}

5. Dao-Schicht-Schreiben

1). OrderPartitionByIdDao

Paket com.bytearch.fast.cloud.mysql.sharding.dao;

importiere com.bytearch.fast.cloud.mysql.sharding.common.ShardingStrategyConstant;
importiere com.bytearch.fast.cloud.mysql.sharding.pojo.entity.OrderEntity;
importiere com.bytearch.fast.cloud.mysql.sharding.strategy.OrderIdShardingStrategy;
importiere org.jfaster.mango.annotation.*;

@DB(Name = ShardingStrategyConstant.LOGIC_ORDER_DATABASE_NAME, Tabelle = "Bestellung")
@Sharding(shardingStrategy = OrderIdShardingStrategy.Klasse)
öffentliche Schnittstelle OrderPartitionByIdDao {

    @SQL("INSERT INTO #table (Bestell-ID, Benutzer-ID, Status, Buchungsdatum, Erstellungszeit, Aktualisierungszeit) WERTE" +
            "(:Bestell-ID,:Benutzer-ID,:Status,:Buchungsdatum,:Erstellungszeit,:Aktualisierungszeit)"
    )
    int insertOrder(@TableShardingBy("orderId") @DatabaseShardingBy("orderId") Bestellentity Bestellentity);

    @SQL("UPDATE #table setze update_time = jetzt()" +
            "#wenn(:Buchungsdatum != null),Buchungsdatum = :Buchungsdatum #Ende " +
            "#wenn (:status != null), status = :status #ende" +
            „WO Bestell-ID = :Bestell-ID“
    )
    int updateOrderByOrderId(@TableShardingBy("orderId") @DatabaseShardingBy("orderId") OrderEntity orderEntity);


    @SQL("AUSWÄHLEN * AUS #Tabelle WO Bestell-ID = :1")
    OrderEntity getOrderById(@TableShardingBy @DatabaseShardingBy Lange Bestell-ID);

    @SQL("AUSWÄHLEN * AUS #Tabelle WO Bestell-ID = :1")
    @UseMaster
    OrderEntity getOrderByIdFromMaster(@TableShardingBy @DatabaseShardingBy Lange Bestell-ID);

6. Unit-Tests

@SpringBootTest(Klassen = {Anwendung.Klasse})
@RunWith(SpringJUnit4ClassRunner.class)
öffentliche Klasse ShardingTest {
    @Autowired
    OrderPartitionByIdDao orderPartitionByIdDao;

    @Autowired
    OrderPartitionByUserIdDao orderPartitionByUserIdDao;

    @Prüfen
    öffentliche void testCreateOrderRandom() {
        für (int i = 0; i < 20; i++) {
            Int Benutzer-ID = ThreadLocalRandom.current().nextInt(1000,1000000);
            Bestellentity Bestellentity = neue Bestellentity();
            orderEntity.setOrderId(SeqIdUtil.nextId(userId % ShardingStrategyConstant.SHARDING_TABLE_NUM));
            orderEntity.setStatus(1);
            orderEntity.setUserId(Benutzer-ID);
            orderEntity.setCreateTime(neues Datum());
            orderEntity.setUpdateTime(neues Datum());
            orderEntity.setBookingDate(neues Datum());
            int ret = orderPartitionByIdDao.insertOrder(orderEntity);
            Assert.assertEquals(1, ret);
        }
    }

    @Prüfen
    public void testOrderAll() {
        //einfügen
        Int Benutzer-ID = ThreadLocalRandom.current().nextInt(1000,1000000);
        Bestellentity Bestellentity = neue Bestellentity();
        orderEntity.setOrderId(SeqIdUtil.nextId(userId % ShardingStrategyConstant.SHARDING_TABLE_NUM));
        orderEntity.setStatus(1);
        orderEntity.setUserId(Benutzer-ID);
        orderEntity.setCreateTime(neues Datum());
        orderEntity.setUpdateTime(neues Datum());
        orderEntity.setBookingDate(neues Datum());
        int i = orderPartitionByIdDao.insertOrder(orderEntity);
        : Assert.assertEquals(1, i);

        //vom Master holen
        OrderEntity orderInfo = orderPartitionByIdDao.getOrderByIdFromMaster(orderEntity.getOrderId());
        Assert.assertNotNull(orderInfo);
        Assert.assertEquals(orderInfo.getOrderId(), orderEntity.getOrderId());

        //vom Slave holen
        OrderEntity slaveOrderInfo = orderPartitionByIdDao.getOrderById(orderEntity.getOrderId());
        Assert.assertNotNull(slaveOrderInfo);
        //aktualisieren
        OrderEntity updateEntity = neue OrderEntity();
        updateEntity.setOrderId(orderInfo.getOrderId());
        updateEntity.setStatus(2);
        updateEntity.setUpdateTime(neues Datum());
        int affectRows = orderPartitionByIdDao.updateOrderByOrderId(updateEntity);
        Assert.assertTrue( affectRows > 0);
    }

    @Prüfen
    öffentliche void testGetListByUserId() {
        Int Benutzer-ID = ThreadLocalRandom.current().nextInt(1000,1000000);
        für (int i = 0; i < 5; i++) {
            Bestellentity Bestellentity = neue Bestellentity();
            orderEntity.setOrderId(SeqIdUtil.nextId(userId % ShardingStrategyConstant.SHARDING_TABLE_NUM));
            orderEntity.setStatus(1);
            orderEntity.setUserId(Benutzer-ID);
            orderEntity.setCreateTime(neues Datum());
            orderEntity.setUpdateTime(neues Datum());
            orderEntity.setBookingDate(neues Datum());
            orderPartitionByIdDao.insertOrder(orderEntity);
        }
        versuchen {
            //Verhindern Sie durch Master-Slave-Verzögerung verursachte Überprüfungsfehler Thread.sleep(1000);
        } Fang (UnterbrocheneAusnahme e) {
            e.printStackTrace();
        }
        Liste<OrderEntity> orderListByUserId = orderPartitionByUserIdDao.getOrderListByUserId(userId);
        Assert.assertNotNull(orderListByUserId);
        Assert.assertTrue(orderListByUserId.size() == 5);
    }
}

Sie sind fertig:

IV. Fazit

Dieser Artikel stellt hauptsächlich die praktische Implementierung von MySQL-Sharding unter Verwendung des Mango-Frameworks in der Java-Version vor. Die Sharding-Middleware kann auch etwas Ähnliches wie ShardingJDBC verwenden oder selbst entwickelt werden.

Die obige Anzahl der Unterdatenbanken und Untertabellen dient nur zu Demonstrationszwecken. In der Praxis wird die Anzahl der Untertabellen und Unterdatenbanken auf Grundlage der tatsächlichen Wachstumsrate der Geschäftsdaten des Unternehmens, des Spitzen-QPS, der physischen Maschinenkonfiguration und anderer Faktoren berechnet.

Damit ist dieser Artikel über die praktische Anwendung von MySQL-Sharding bei der Auftragsrekonstruktion abgeschlossen. Weitere Informationen zu MySQL-Sharding finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, Sie werden 123WORDPRESS.COM auch in Zukunft unterstützen!

Das könnte Sie auch interessieren:
  • Erste Schritte mit MySQL Sharding
  • MySQL-Sharding-Details
  • Zusammenfassung der häufig verwendeten Datenbank- und Tabellen-Sharding-Lösungen von MySQL
  • MySQL-Datenbank-Sharding und Tabellen-Sharding sind vollständig zusammengebrochen
  • Mehrere Methoden der Primärschlüsselverarbeitung nach MySQL-Datenbank- und Tabellen-Sharding
  • SpringBoot+MybatisPlus+Mysql+Sharding-JDBC-Sharding
  • Mehrere Möglichkeiten zum Sharding von MySQL-Datenbanken und -Tabellen

<<:  Wissenspunkte und Nutzungsdetails zu globalen Variablen und globalen Objekten von Nodejs

>>:  Lösung für das Problem, dass der Text unten links steht und bei der Gestaltung des Textbereichs nicht in der Größe geändert werden kann

Artikel empfehlen

Detaillierte grafische Erklärung zum Löschen des Keep-Alive-Cache

Inhaltsverzeichnis Eröffnungsszene Direktes Rende...

Richtige Verwendung der Vue-Funktion Anti-Shake und Throttling

Vorwort 1. Entprellen: Nach dem Auslösen eines Ho...

Native JS-Kapselung, nahtlose Karussellfunktion

Natives js-gekapseltes nahtloses Karussell-Plug-I...

Vue implementiert eine kleine Wettervorhersageanwendung

Dies ist eine Website, die ich nachgeahmt habe, a...

Besser aussehende benutzerdefinierte CSS-Stile (Titel h1 h2 h3)

Rendern Häufig verwendete Stile im Blog Garden /*...

So verstehen Sie die Dateninteraktion und -anzeige im Vue-Frontend und -Backend

Inhaltsverzeichnis 1. Technischer Überblick 2. Te...

Vue implementiert grafischen Überprüfungscode

In diesem Artikelbeispiel wird der spezifische Co...

Vite führt die Implementierung virtueller Dateien ein

Inhaltsverzeichnis Hintergrund Virtuelle Dateien ...

Vue implementiert eine Formularvalidierungsfunktion

Dieser Artikel beschreibt hauptsächlich, wie die ...

Details zu den Überwachungseigenschaften der Uhr in Vue

Inhaltsverzeichnis 1.watch überwacht Änderungen i...

Detaillierte Erklärung zur Verwendung des Grep-Befehls in Linux

Linux-Grep-Befehl Mit dem Linux-Befehl grep könne...

VUE + SpringBoot implementiert die Paging-Funktion

Dieser Artikel stellt hauptsächlich vor, wie pagi...