2015-12-22 25 views
5

Ich muss die Größe der MySQL-Datenbank reduzieren. Ich habe einige Informationen umgeschrieben, die ';' und ':' von sources Spalte (~ 10% Verkürzung). Danach ist die Größe der Tabelle genau gleich wie zuvor. Wie ist es möglich? Ich benutze MyISAM Engine.Warum ist die Größe der MySQL MyISAM-Tabelle nach dem Striping einiger Daten aus der VARCHAR-Spalte gleich?

BTW: Leider kann ich die Tabellen mit myisampack nicht komprimieren.

mysql> INSERT INTO test SELECT protid1, protid2, CS, REPLACE(REPLACE(sources, ':', ''), ';', '') FROM homologs_9606; 
Query OK, 41917131 rows affected (4 min 11.30 sec) 
Records: 41917131 Duplicates: 0 Warnings: 0 

mysql> select TABLE_NAME name, ROUND(TABLE_ROWS/1e6, 3) 'million rows', ROUND(DATA_LENGTH/power(2,30), 3) 'data GB', ROUND(INDEX_LENGTH/power(2,30), 3) 'index GB' from information_schema.TABLES WHERE TABLE_NAME IN ('homologs_9606', 'test') ORDER BY TABLE_ROWS DESC LIMIT 10; 
+---------------+--------------+---------+----------+ 
| name   | million rows | data GB | index GB | 
+---------------+--------------+---------+----------+ 
| test   |  41.917 | 0.857 | 1.075 | 
| homologs_9606 |  41.917 | 0.887 | 1.075 | 
+---------------+--------------+---------+----------+ 
2 rows in set (0.01 sec) 

mysql> select * from homologs_9606 limit 10; 
+---------+---------+-------+--------------------------------+ 
| protid1 | protid2 | CS | sources      | 
+---------+---------+-------+--------------------------------+ 
| 5635338 | 1028608 | 0.000 | 10:,1       | 
| 5644385 | 1028611 | 0.947 | 5:1,1;8:0.943,35;10:1,1;11:1,1 | 
| 5652325 | 1028611 | 0.947 | 5:1,1;8:0.943,35;10:1,1;11:1,1 | 
| 5641128 | 1028612 | 1.000 | 8:1,10       | 
| 5636414 | 1028616 | 0.038 | 8:0.038,104;10:,1    | 
| 5636557 | 1028616 | 0.000 | 8:,4       | 
| 5637419 | 1028616 | 0.011 | 5:,1;8:0.011,91;10:,1   | 
| 5641196 | 1028616 | 0.080 | 5:1,1;8:0.074,94;10:,1;11:,4 | 
| 5642914 | 1028616 | 0.000 | 8:,3       | 
| 5643778 | 1028616 | 0.056 | 8:0.057,70;10:,1    | 
+---------+---------+-------+--------------------------------+ 
10 rows in set (4.55 sec) 

mysql> select * from test limit 10; 
+---------+---------+-------+-------------------------+ 
| protid1 | protid2 | CS | sources     | 
+---------+---------+-------+-------------------------+ 
| 5635338 | 1028608 | 0.000 | 10,1     | 
| 5644385 | 1028611 | 0.947 | 51,180.943,35101,1111,1 | 
| 5652325 | 1028611 | 0.947 | 51,180.943,35101,1111,1 | 
| 5641128 | 1028612 | 1.000 | 81,10     | 
| 5636414 | 1028616 | 0.038 | 80.038,10410,1   | 
| 5636557 | 1028616 | 0.000 | 8,4      | 
| 5637419 | 1028616 | 0.011 | 5,180.011,9110,1  | 
| 5641196 | 1028616 | 0.080 | 51,180.074,9410,111,4 | 
| 5642914 | 1028616 | 0.000 | 8,3      | 
| 5643778 | 1028616 | 0.056 | 80.057,7010,1   | 
+---------+---------+-------+-------------------------+ 
10 rows in set (0.00 sec) 

mysql> describe test; 
+---------+------------------+------+-----+---------+-------+ 
| Field | Type    | Null | Key | Default | Extra | 
+---------+------------------+------+-----+---------+-------+ 
| protid1 | int(10) unsigned | YES | PRI | NULL |  | 
| protid2 | int(10) unsigned | YES | PRI | NULL |  | 
| CS  | float(4,3)  | YES |  | NULL |  | 
| sources | varchar(100)  | YES |  | NULL |  | 
+---------+------------------+------+-----+---------+-------+ 
4 rows in set (0.00 sec) 

mysql> describe homologs_9606; 
+---------+------------------+------+-----+---------+-------+ 
| Field | Type    | Null | Key | Default | Extra | 
+---------+------------------+------+-----+---------+-------+ 
| protid1 | int(10) unsigned | NO | PRI | 0  |  | 
| protid2 | int(10) unsigned | NO | PRI | 0  |  | 
| CS  | float(4,3)  | YES |  | NULL |  | 
| sources | varchar(100)  | YES |  | NULL |  | 
+---------+------------------+------+-----+---------+-------+ 
4 rows in set (0.00 sec) 

EDIT1: hinzugefügt durchschnittliche Säulenlänge.

mysql> select AVG(LENGTH(sources)) from test; 
+----------------------+ 
| AVG(LENGTH(sources)) | 
+----------------------+ 
|    5.2177 | 
+----------------------+ 
1 row in set (10.04 sec) 

mysql> select AVG(LENGTH(sources)) from homologs_9606; 
+----------------------+ 
| AVG(LENGTH(sources)) | 
+----------------------+ 
|    6.8792 | 
+----------------------+ 
1 row in set (9.95 sec) 

EDIT2: indem NOT NULL auf alle Spalten konnte ich einige mehr MB abzustreifen.

mysql> drop table test 
Query OK, 0 rows affected (0.42 sec) 

mysql> CREATE table test (protid1 INT UNSIGNED NOT NULL DEFAULT '0', protid2 INT UNSIGNED NOT NULL DEFAULT '0', CS FLOAT(4,3) NOT NULL DEFAULT '0', sources VARCHAR(100) NOT NULL DEFAULT '0', PRIMARY KEY (protid1, protid2), KEY `idx_protid2` (protid2)) ENGINE=MyISAM CHARSET=ascii; 
Query OK, 0 rows affected (0.06 sec) 

mysql> INSERT INTO test SELECT protid1, protid2, CS, REPLACE(REPLACE(sources, ':', ''), ';', '') FROM homologs_9606; 
Query OK, 41917131 rows affected (2 min 7.84 sec) 

mysql> select TABLE_NAME name, ROUND(TABLE_ROWS/1e6, 3) 'million rows', ROUND(DATA_LENGTH/power(2,30), 3) 'data GB', ROUND(INDEX_LENGTH/power(2,30), 3) 'index GB' from information_schema.TABLES WHERE TABLE_NAME IN ('homologs_9606', 'test'); 
Records: 41917131 Duplicates: 0 Warnings: 0 

+---------------+--------------+---------+----------+ 
| name   | million rows | data GB | index GB | 
+---------------+--------------+---------+----------+ 
| homologs_9606 |  41.917 | 0.887 | 1.075 | 
| test   |  41.917 | 0.842 | 1.075 | 
+---------------+--------------+---------+----------+ 
2 rows in set (0.02 sec) 
+1

Haben Sie 'OPTIMIZE TABLE' ausgeführt? – Jaco

+0

ja, die Größe ist gleich. – Leszek

+0

Kann AVG (LÄNGE (Quellen)) für beide Tabellen enthalten? Und auch der Zeichensatz für jede Tabelle ('SHOW CREATE TABLE tablename' ist besser als' DESCRIBE tablename') –

Antwort

2

Sie sind nicht genau gleich. Ihre Abfrage zeigt deutlich, dass test ist etwa 30 MB kleiner als homologs_9606:

+---------------+--------------+---------+ 
| name   | million rows | data GB | 
+---------------+--------------+---------+ 
| test   |  41.917 | 0.857 | <-- 0.857 < 0.887 
| homologs_9606 |  41.917 | 0.887 | 
+---------------+--------------+---------+ 

Wie viel Speicher sollten wir für Ihren Tisch zu erwarten? Lassen Sie uns Data Type Storage Requirements überprüfen:

INTEGER(10): 4 bytes 
FLOAT(4): 4 bytes 
VARCHAR(100): L+1 

wobei L die Anzahl der Zeichen Bytes, die in der Regel ein Byte pro Zeichen ist, aber manchmal mehr, wenn Sie einen Unicode-Zeichensatz verwenden.

Ihre Reihen im Durchschnitt benötigen:

INTEGER + INTEGER + FLOAT + VARCHAR = 
4 + 4 + 4 + (L + 1) = L + 13 bytes 

Wir ursprünglichen Durchschnitt L wie (0.887*1024^3/41917131) - 13 = 9.72 ableiten kann. Sie sagen, dass Sie 10% von sources abgezogen haben, was bedeutet, dass Ihr neues L 9.72*0.9 = 8.75 ist. Das gibt einen erwarteten neuen Gesamtspeicherbedarf von ((8.75 + 13) * 41917131)/1024^3 = 0.849 GB

Ich vermute, dass die Differenz (zwischen 0.849 und 0.857) aufgrund der Tatsache, könnte sein, dass test haben zwei Spalten als Nullable eingestellt, dass homologs_9606 nicht haben, aber mir do not know enough über die MyISAM-Engine, um genau zu berechnen. Ich kann aber raten! Auf einem Minimum würden Sie 1 Bit pro Spalte pro Zeile benötigen, um einen NULL-Zustand zu speichern, was in Ihrem Fall zwei Bits pro Zeile oder 2*41917131 = 83834262 bits = 10 479 283 bytes = 0.010 GB bedeutet. Die Summe 0.849+0.010 = 0.859 schießt knapp über das Ziel (ca. 2 MB zu viel). Aber ich habe ein paar Rundungen gemacht und deine 10% Zahl ist auch ein Schätzwert, also bin ich mir sicher, dass der Rest in der Übersetzung verloren geht.

könnte ein weiterer Grund sein, wenn Sie ein Unicode-Zeichen auf sources in test gesetzt verwenden, wobei in diesem Fall einige Zeichen jeweils mehr als ein Byte verwenden, aber da die Nullable-Spalten scheint für alles zu erklären Ich glaube nicht, dass dies das ist Fall für Ihren Tisch.

Zusammenfassung

  • Ihre beiden Tabellen sind nicht gleich groß, sie unterscheiden sich von 30 MB.
  • Die Größe Ihrer neuen Tabelle entspricht ungefähr der erwarteten Größe.
  • Sie können mehr Speicherplatz in Ihrer neuen Tabelle speichern, indem Sie protid1 und protid2 in NOT NULL Spalten machen.
+0

Danke für die sehr nachdenkliche Antwort, Emil! Ich werde NOT NULL Werte einstellen. – Leszek

+0

Ich weiß sehr wenig über Ihre Daten, aber Sie sollten vielleicht in Erwägung ziehen, die Integer-Spalten auf MEDIUMINT * zu setzen, wenn * Sie den zusätzlichen Bereich nicht benötigen (siehe [Integer-Typen] (https://dev.mysql.com/doc/ refman/5.7/de/integer-types.html) für den exakten Bereich). Das würde ungefähr 40 MB pro Spalte sparen. –

+0

Guter Hinweis. Ich habe es vorher versucht, aber die protid1/2 kann max MEDIUMINT in naher Zukunft überschreiten. Wie auch immer, die Verwendung von MEDIUMINT gibt einen weiteren 19Mb (0.823GB) frei. – Leszek

0

Die "Tabelle" wird in einer .MYD Datei gespeichert. Diese Datei wird nie schrumpfen wegen UPDATEs oder DELETEs. SHOW TABLE STATUS (oder die entsprechende Abfrage in information_schema) kann Data_length verkleinern, aber Data_free wird zunehmen.

Sie können die .MYD Datei verkleinern, indem Sie OPTIMIZE TABLE tun. Aber das wird die Tabelle kopieren, wodurch zusätzlicher Speicherplatz während des Prozesses benötigt wird. Und diese Aktion lohnt sich nur sehr selten.

Die Änderung an NOT NULL kann nicht Speicherplatz freigeben, wenn Sie viele Nullen hatten - "" dauert 1 oder 2 Bytes für eine VARCHAR wegen der Länge. (Und Ihr Code muss möglicherweise '' anders als NULL behandeln.)

Der Platz für jede Zeile genommen ist tatsächlich 1 Byte mehr als zuvor erwähnt - dieses Byte behandelt zu wissen, ob die Zeile vorhanden ist oder der Beginn eines Lochs ist .

Für große Textfelder möchte ich dies tun, um Platz zu sparen. (Dies gilt sowohl für MyISAM als auch für InnoDB.) Komprimieren Sie den Text und speichern Sie ihn in einer BLOB Spalte (anstelle von TEXT). Für den meisten Text ist das eine Schrumpfung von 3: 1. Es benötigt ein wenig zusätzlichen Code und CPU-Zeit im Client, aber es spart viel E/A im Server. Oft ist das Nettoergebnis "schneller". Ich würde es nicht für das Varchar verwenden, das du hast; Ich würde es nur auf Spalten machen, die größer sind als, sagen wir, 50 Zeichen Durchschnitt.

Zurück zur ursprünglichen Frage. Es klingt, als gäbe es nur etwa 30 Millionen Doppelpunkte und Semikolons in der gesamten Tabelle. Könnte es sein, dass die ersten 10 Zeilen nicht repräsentativ sind?

+0

Wenn Sie das in der Frage enthaltene Schema lesen, werden Sie sehen, dass die ursprüngliche Tabelle nur NOT NULL-Felder enthält, so dass sie ohne NOT NULL in der neuen Tabelle geändert werden können. –