sql >> Databasteknik >  >> RDS >> Mysql

MySQL-prestanda:flera tabeller kontra index på enstaka tabeller och partitioner

Att skapa 20 000 bord är en dålig idé. Du behöver 40 000 bord inom kort, och sedan mer.

Jag kallade detta syndrom för Metadata Tribbles i min bok SQL-antimönster . Du ser detta hända varje gång du planerar att skapa en "tabell per X" eller en "kolumn per X".

Detta orsakar verkliga prestandaproblem när du har tiotusentals bord. Varje tabell kräver MySQL för att upprätthålla interna datastrukturer, filbeskrivningar, en dataordbok, etc.

Det finns också praktiska operativa konsekvenser. Vill du verkligen skapa ett system som kräver att du skapar en ny tabell varje gång en ny användare registrerar sig?

Istället skulle jag rekommendera att du använder MySQL-partitionering .

Här är ett exempel på partitionering av tabellen:

CREATE TABLE statistics (
  id INT AUTO_INCREMENT NOT NULL,
  user_id INT NOT NULL,
  PRIMARY KEY (id, user_id)
) PARTITION BY HASH(user_id) PARTITIONS 101;

Detta ger dig fördelen av att definiera en logisk tabell, samtidigt som du delar upp tabellen i många fysiska tabeller för snabbare åtkomst när du frågar efter ett specifikt värde på partitionsnyckeln.

Till exempel, när du kör en fråga som ditt exempel, kommer MySQL endast åt rätt partition som innehåller det specifika användar-id:

mysql> EXPLAIN PARTITIONS SELECT * FROM statistics WHERE user_id = 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: statistics
   partitions: p1    <--- this shows it touches only one partition 
         type: index
possible_keys: NULL
          key: PRIMARY
      key_len: 8
          ref: NULL
         rows: 2
        Extra: Using where; Using index

HASH-metoden för partitionering innebär att raderna placeras i en partition med en modul för heltalspartitionsnyckeln. Detta betyder att många user_id:s mappar till samma partition, men varje partition skulle bara ha 1/N:e så många rader i genomsnitt (där N är antalet partitioner). Och du definierar tabellen med ett konstant antal partitioner, så att du inte behöver utöka den varje gång du får en ny användare.

Du kan välja valfritt antal partitioner upp till 1024 (eller 8192 i MySQL 5.6), men vissa personer har rapporterat prestandaproblem när de blir så höga.

Det rekommenderas att använda ett primtal av partitioner. Om dina user_id-värden följer ett mönster (som att endast använda jämna tal), hjälper det att använda ett primtal av partitioner att fördela data jämnare.

Angående dina frågor i kommentaren:

För HASH-partitionering, om du använder 101 partitioner som jag visar i exemplet ovan, så har varje given partition cirka 1% av dina rader i genomsnitt. Du sa att din statistiktabell har 30 miljoner rader, så om du använder den här partitioneringen skulle du bara ha 300 000 rader per partition. Det är mycket lättare för MySQL att läsa igenom. Du kan (och bör) använda index också -- varje partition kommer att ha sitt eget index, och det kommer bara att vara 1 % så stort som indexet på hela den opartitionerade tabellen skulle vara.

Så svaret på hur kan du bestämma ett rimligt antal partitioner är:hur stort är hela ditt bord, och hur stora vill du att partitionerna ska vara i genomsnitt?

Antalet partitioner behöver inte nödvändigtvis växa om du använder HASH-partitionering. Så småningom kan du ha 30 miljarder rader totalt, men jag har upptäckt att när din datavolym växer i storleksordningar, kräver det en ny arkitektur ändå. Om din data växer så stor behöver du förmodligen skärning över flera servrar samt partitionering i flera tabeller.

Som sagt, du kan partitionera om en tabell med ALTER TABLE:

ALTER TABLE statistics PARTITION BY HASH(user_id) PARTITIONS 401;

Detta måste omstrukturera tabellen (som de flesta ändringar av ALTER TABLE), så förvänta dig att det tar ett tag.

Du kanske vill övervaka storleken på data och index i partitioner:

SELECT table_schema, table_name, table_rows, data_length, index_length
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE partition_method IS NOT NULL;

Som med vilken tabell som helst vill du att den totala storleken på aktiva index ska passa i din buffertpool, för om MySQL måste byta delar av index in och ut ur buffertpoolen under SELECT-frågor, blir prestanda lidande.

Om du använder RANGE- eller LIST-partitionering är det mycket vanligare att lägga till, släppa, slå samman och dela upp partitioner. Se http://dev.mysql. com/doc/refman/5.6/en/partitioning-management-range-list.html

Jag uppmuntrar dig att läsa manualavsnittet om partitionering , och kolla även in den här trevliga presentationen:Boost prestanda Med MySQL 5.1-partitioner .



  1. Lagring av UTF-16/Unicode-data i SQL Server

  2. Hur infogar jag flera värden i en postgres-tabell samtidigt?

  3. Vad är ett databasschema?

  4. Hur konverterar jag ett skript med mysql_-funktioner till att använda mysqli_-funktioner?