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

Vue3 (Teil 2) Integration von Ant Design Vue

Inhaltsverzeichnis 1. Integrieren Sie Ant Design ...

CSS Skills Collection – Klassiker unter den Klassikern

Entfernen Sie das gepunktete Kästchen auf dem Link...

Detaillierte Erklärung zur Verwendung von Filtereigenschaften in CSS

Das Filterattribut definiert die visuelle Wirkung...

JS implementiert die Append-Funktion von jQuery

Inhaltsverzeichnis Zeig mir den Code Testen Sie d...

Lösen Sie das Problem der Angabe der UDP-Portnummer im Docker

Wenn Docker einen Container startet, gibt es den ...

So aktivieren Sie die MySQL-Remoteverbindung

Aus Sicherheitsgründen erlaubt MySql-Server nur d...

Beispiel für die Installation von nginx in einem angegebenen Verzeichnis

Aufgrund von Unternehmensanforderungen müssen zwe...

Lösung für den Fehler von 6ull beim Laden des Linux-Treibermoduls

Inhaltsverzeichnis 0x01 Das Treibermodul konnte n...

CentOS 8-Installationsdiagramm (superdetailliertes Tutorial)

CentOS 8 ist offiziell veröffentlicht! CentOS ent...

So installieren Sie MySQL unter Linux (Yum und Quellcode-Kompilierung)

Es gibt zwei Möglichkeiten, MySQL unter Linux zu ...

Detaillierte Erklärung des Prinzips und der Verwendung von MySQL-Ansichten

Dieser Artikel veranschaulicht anhand von Beispie...

Beispiele für den Import und Export von MySQL-Tabellendaten

Dieser Artikel beschreibt die Import- und Exportv...