SQL-Optimierungen

Werbung
Tag 7
Inhaltsverzeichnis
• Das DB-Testdaten Problem
• Verschiedene Werkzeuge
• Überblick über "benerator"
• Testdaten für DB GMCD2 generieren
• Installation und Übungen mit "benerator"
• SQL-Optimierungen
• Was macht der Optimizer?
• Problematik der Datenzugriff mit SQL
• Anwendungsfall GMCD2 und MySQL
• Übungen
• Ein Blick in die Zukunft
• RDB vs. NoSQL
RDB 7 - 1
Version 3.5
DB-Testdaten
Das Problem
Daten
frisch erstellen
produktive Daten
anonymisieren
Beispiele:
- gmcd
- EqualS
- ...
RDB 7 - 2
Version 3.5
DB-Testdaten
Werkzeuge
• SQL-Scripte von Hand entwickeln
• Perl-Script / Java-Program selber entwickeln
• Datengenerator verwenden
Zum Beispiel:
•
•
•
•
benerator
TurboData
Jailer
...
Quelle: http://databene.org/databene-benerator/similar-products.html
RDB 7 - 3
Version 3.5
DB-Testdaten
Das benerator Werkzeug
Quelle: http://databene.org/databene-benerator.html
RDB 7 - 4
Version 3.5
DB-Testdaten
Wie funktioniert benerator?
Quelle: http://databene.org/databene-benerator.html
RDB 7 - 5
Version 3.5
DB-Testdaten
benerator Komponenten
Quelle: http://databene.org/download/databene-benerator-manual-0.8.1.pdf
RDB 7 - 6
Version 3.5
DB-Testdaten
benerator Beispiel für GMCD2 (1)
Inhalt der Datei "gmcd2.benerator.xml"
<?xml version="1.0" encoding="iso-8859-1"?>
<setup
xmlns="http://databene.org/benerator/0.7.8"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://databene.org/benerator/0.7.8
http://databene.org/benerator-0.7.8.xsd">
<comment>
Subject: MySQL Benerator Populator for DB GMCD2
Author : Gilles Maitre, 2012
Links :
</comment>
...
RDB 7 - 7
Version 3.5
DB-Testdaten
benerator Beispiel für GMCD2 (2)
Inhalt der Datei "gmcd2.benerator.xml"
...
<import defaults="true"
domains="person,net,product,address,organization"
platforms="db" />
<comment>setting default values</comment>
<setting name="dbCatalog" value="" />
<setting name="dbSchema" value="" />
<setting name="dbBatch" value="false" />
...
RDB 7 - 8
Version 3.5
DB-Testdaten
benerator Beispiel für GMCD2 (3)
Inhalt der Datei "gmcd2.benerator.xml"
...
...
<comment>import database properties' file</comment>
<include uri="gmcd2.benerator.properties" />
Inhalt der Datei "gmcd2.benerator.properties"
dbType=MySQL
dbUrl=jdbc:mysql://localhost/GMCD2
dbDriver=com.mysql.jdbc.Driver
dbSchema=GMCD2
dbCatalog=GMCD2
dbUser=gmaitre
dbPassword=Gilles
nbrOfMusikerPerLandAndSex=25
nbrOfStueck=20
nbrOfCD=10
RDB 7 - 9
Version 3.5
DB-Testdaten
benerator Beispiel für GMCD2 (4)
Inhalt der Datei "gmcd2.benerator.xml"
<comment>define a database, later referred by the id 'db'</comment>
<database id="db" url="{dbUrl}" driver="{dbDriver}"
catalog="{dbCatalog}" user="{dbUser}"
password="{dbPassword}" batch="{dbBatch}" />
<comment>drop current tables if they exist and recreate them</comment>
<execute uri="{ftl:drop${dbType}Tables.sql}" target="db" onError="ignore" />
<execute uri="{ftl:create${dbType}Tables.sql}" target="db" />
Inhalt der Datei "dropMySQLTables.sql"
drop table
drop table
drop table
drop table
drop table
drop table
drop table
commit;
tbl_MusikerInstrument;
tbl_StueckMusiker;
tbl_CDStueck;
tbl_CD;
tbl_Instrument;
tbl_Musiker;
tbl_Stueck;
RDB 7 - 10
Version 3.5
DB-Testdaten
benerator Beispiel für GMCD2 (5)
Inhalt der Datei "gmcd2.benerator.xml"
<comment>Importing Instruments from CSV file called "Instrument.csv"</comment>
<iterate source="Instrument.csv" type="tbl_Instrument"
encoding="utf-8" consumer="db" />
Inhalt der Datei "Instrument.csv"
ID,Typ,Name
1,Saiteninstrument,Gitarre
2,Saiteninstrument,Bass
...
Inhalt der DB
mysql> select * from tbl_Instrument;
+----+-------------------+----------------+
| ID | Typ
| Name
|
+----+-------------------+----------------+
| 1 | Saiteninstrument | Gitarre
|
| 2 | Saiteninstrument | Bass
|
...
RDB 7 - 11
Version 3.5
DB-Testdaten
benerator Beispiel für GMCD2 (6)
Inhalt der Datei "gmcd2.benerator.xml"
<comment>Generate Musiker of different countries / MALE and FEMALE</comment>
<comment>First, define a primary key generator for Musiker
starting at 1000</comment>
<bean id="idGenMusiker" spec="new IncrementGenerator(1000)" />
<comment>Generate Brasilian MALE Musiker</comment>
<generate type="tbl_Musiker" count="{nbrOfMusikerPerLandAndSex}" consumer="db">
<id name="ID" generator="idGenMusiker" />
<attribute name="Vorname"
generator="new GivenNameGenerator('BR', Gender.MALE)" />
<attribute name="Name" generator="new FamilyNameGenerator('BR')" />
</generate>
Inhalt der DB
mysql> select * from tbl_Musiker;
+------+--------------+------------+
| ID
| Vorname
| Name
|
+------+--------------+------------+
| 1000 | Danilo
| Ribeiro
|
| 1001 | Bruno
| Oliveira
|
| 1002 | Mateus
| Pinto
|
RDB 7 - 12
Version 3.5
DB-Testdaten
benerator Beispiel für GMCD2 (7)
Inhalt der Datei "gmcd2.benerator.xml"
<iterate type="tbl_Musiker" name="musiker" source="db">
<generate type="tbl_MusikerInstrument"
minCount="1" maxCount="3" consumer="db">
<!-- An Hand der Liste Musikers, vergebe jedem 1 bis 3 Instrumente -->
<attribute name="MusikerID" type="long" script="{musiker.ID}"/>
<reference name="InstrumentID" source="db"
distribution="random" unique="true"/>
</generate>
</iterate>
Inhalt der DB, Instrumenteverteilung:
mysql> select Name, count(*) from tbl_MusikerInstrument mi,
tbl_Instrument i where mi.InstrumentID=i.ID group by
InstrumentID;
+----------------+----------+
| Name
| count(*) |
+----------------+----------+
| Gitarre
|
9 |
| Bass
|
22 |
| Gesang
|
14 |
| Schlagzeug
|
14 |
RDB 7 - 13
...
Version 3.5
DB-Testdaten
benerator Beispiel für GMCD2 (8)
Inhalt der Datei "gmcd2.benerator.xml"
<comment>Generate Stueck</comment>
<generate type="tbl_Stueck" count="{nbrOfStueck}" consumer="db">
<attribute name="AufnahmeDatum"
generator="new DateGenerator('1958-01-01', '2011-02-15', 1)" />
<attribute name="Dauer" min="10" max="1000"
distribution="new GaussianFunction(240, 120)"/>
</generate>
Inhalt der DB, "Dauer" Verteilung:
mysql> select min(Dauer), max(Dauer), avg(Dauer) from tbl_Stueck;
+------------+------------+------------+
| min(Dauer) | max(Dauer) | avg(Dauer) |
+------------+------------+------------+
|
81 |
418 |
252.6000 |
+------------+------------+------------+
RDB 7 - 14
Version 3.5
DB-Testdaten
benerator Installation und Test mit GMCD2
•
•
•
•
Software herunterladen ab http://bergmann-it.de/download/download_benerator
und ins <Ihr benerator Installationsverzeichnis> dekomprimieren.
Datei http://www.sws.bfh.ch/maitre/SD-RDB/gmcd2.zip
in ein Arbeitsverzeichnis dekomprimieren.
In der Datei "gmcd2.benerator.properties", Benutzername und Passwort anpassen.
Mit einem mysql-Interpreter oder MySQL Workbench, als "root":
• create database GMCD2;
grant all privileges on GMCD2.* to <yourUser>@localhost identified by "<your
password>";
In einem DOS-Fenster (Achtung: auf Linux sieht es anders aus):
•
•
•
cd <Ihr Arbeitsverzeichnis>
•
set BENERATOR_HOME=<benerator Installationsverzeichnis>
•
set Path=%BENERATOR_HOME%\bin;%Path%
•
set BENERATOR_OPTS=-Dstage=development -Ddatabase=mysql
•
Alles auf einer Zeile folgenden Befehl in einem DOS-Fenster ausführen:
java -classpath .;%BENERATOR_HOME%/bin/;%BENERATOR_HOME%/lib;
%BENERATOR_HOME%/lib/* org.databene.benerator.main.Benerator
./gmcd2.benerator.xml
•
Schauen Sie den Inhalt der GMCD2 DB an und testen Sie die SQL-Befehle aus
der Datei "gmcd2.benerator.xml" (in Bezug auf Datenverteilung).
RDB 7 - 15
Version 3.5
SQL-Optimierungen
Problematik
• RDB Tag 6: Performance auf Betriebssystem-,
DB-Index- und Applikationsebene
• Jetzt: Fokus auf SQL
• Problem 1: SQL sagt "was" aber nicht "wie"...
• Problem 2: RDB-spezifisch
=> MySQL != Oracle
RDB 7 - 16
Version 3.5
SQL-Optimierungen
Datentypen
•
Wenn möglich, NOT NULL-Felder definieren
(NULL sind schwerer zu optimieren, indexieren)
•
MySQL-Breite für Integer-Typen (INT(1) gleich performant wie
INT(20))
Für interaktive Werkzeuge (z.B. MySQL Command-Interpreter)
•
DECIMAL: mehr Speicher und langsamer als FLOAT und DOUBLE
•
CHAR: gut für kleine Strings
VARCHAR: "richtig dimensionieren", wegen Sortieren
•
DATETIME vs. TIMESTAMP... nicht gleichen Anwendungszweck
(DATETIME braucht mehr Speicher, weniger begrenzt, siehe RDB Tag
3)
•
Achtung: Automatisch-generierte Schemata (z.B. ORM) überprüfen
Quelle: High Performance MySQL, Kapitel 3
RDB 7 - 17
Version 3.5
SQL-Optimierungen
Index- und Datenfragmentierung
• Probleme
• Tabellen können beschädigt sein (wegen Crash)
• B-Bäume Indexe können fragmentiert sein
• InnoDB Index-Benutzung basiert auf Statistiken
(diese sind vielleicht nicht mehr aktuell)
• ==> Tabellen und Indexes (Cardinality) prüfen
• Befehle:
• analyse table <table>
• show index from <table>
• optimize table <table> oder
• alter table <table> engine=innodb
Quelle: High Performance MySQL, Seite 148
RDB 7 - 18
Version 3.5
SQL-Optimierungen
MySQL Abfragebearbeitung
Quelle: High Performance MySQL, Seite 172
RDB 7 - 19
Version 3.5
SQL-Optimierungen
MySQL Client/Server Protokoll
•
Halb-Duplex Protokoll
==
Entweder Client sendet dem Server etwas, oder das Gegenteil
•
<=> Keine Kommunikation "gleichzeitig" zwischen Client und Server
•
==> Keine Möglichkeit, eine Abfrage abzubrechen...
Man muss warten bis alle Daten fertig übertragen werden!
Quelle: High Performance MySQL, Seite 173
RDB 7 - 20
Version 3.5
SQL-Optimierungen
MySQL Abfrage Cache
•
Hash-Tabelle (Abfrage / Resultatsdaten) (Case-sensitive)
Abfrage
Resultatsdaten
select * from ...
...
select ID, XY from ...
...
•
Nicht determinische Abfragen werden nicht gespeichert (Abfragen mit now() oder
current_user())
•
Problem mit Transaktionen: Cache-Lookup erst möglich wenn die Transaktion
abgeschlossen ist
•
SET SESSION query_cache_type = OFF; => Disables CACHE... (Default: ON)
•
Parametrisierung: mit Variablen in my.cnf (my.conf/ini):
query_cache_limit
= 1M
// Max. Grösse des Resultatspuffers
query_cache_size
= 16M // Gesamte Cache-Grösse
•
select ohne Cache-Anwendung ausführen (Demo):
select SQL_NO_CACHE count(*) from tbl_Musiker;
show status like "last_query_cost"; // Effekt... Einheit: x*4kb-Seiten lesen
Quelle: High Performance MySQL, Kapitel 5
RDB 7 - 21
Version 3.5
SQL-Optimierungen
Oracle / MySQL Abfrage Optimierer
• Oracle 10g zwei Optimierer (alt und neu):
• Rule Based Optimizer (RBO): Regel-basiert (alt)
<=> Basiert auf fixen Eigenschaften von Tabellen
(Index, Datentypen)
unabhängig von der Entwicklung der Daten
• Cost Based Optimizer (CBO): Kosten-basiert (neu)
<=> Basiert auf Tabellen-Statistiken (periodisch aktualisiert)
abhängig von der Entwicklung der Daten
• MySQL Optimierer ist "Kosten-basiert"
=> Statistik müssen stimmen (siehe Slide Fragmentierung)
• MySQL Optimierer unabhängig vom Engine (siehe Slide
Abfragebearbeitung)
Quelle: SQL Tuning, Seite 86
RDB 7 - 22
Version 3.5
SQL-Optimierungen
MySQL Nested-Loop-Join's
•
Alle JOIN's im MySQL als Nested-Loop-Join's betrachtet
select ... from A a join B b using(id);
foreach a in A {
foreach b in B {
if a.id == b.id
}
}
•
=> getroffen...
Es gibt aber andere Möglichkeiten zu "joinen"
Quelle: http://de.wikipedia.org/wiki/Nested_Loop_Join
RDB 7 - 23
Version 3.5
SQL-Optimierungen
MySQL Ausführungspläne vergleichen (1)
•
Beispiel: Wie viele Musiker in GMCD2 spielen Gitarre?
•
SELECT count(*)
FROM tbl_Musiker m,
tbl_MusikerInstrument mi,
tbl_Instrument i
WHERE i.Name = 'Gitarre'
AND
i.ID = mi.InstrumentID AND
m.ID = mi.MusikerID;
•
Antwort: 19
•
Last_query_cost: 31.799000
RDB 7 - 24
Version 3.5
SQL-Optimierungen
MySQL Ausführungspläne vergleichen (2)
•
Gleiches Beispiel wie vorher:
•
SELECT count(*)
FROM tbl_Musiker m,
tbl_MusikerInstrument mi,
tbl_Instrument i
WHERE i.Name = 'Gitarre'
AND
i.ID = mi.InstrumentID AND
m.ID = mi.MusikerID;
•
Antwort: 19
•
Last_query_cost: 0.000000... Wieso?
RDB 7 - 25
Version 3.5
SQL-Optimierungen
MySQL Ausführungspläne vergleichen (3)
•
Wir probieren noch einmal, aber ohne Cache diesmal:
•
SELECT SQL_NO_CACHE count(*)
FROM tbl_Musiker m,
tbl_MusikerInstrument mi,
tbl_Instrument i
WHERE i.Name = 'Gitarre'
AND
i.ID = mi.InstrumentID AND
m.ID = mi.MusikerID;
•
Antwort: 19
•
Last_query_cost: 31.799000
RDB 7 - 26
Version 3.5
SQL-Optimierungen
MySQL Ausführungspläne vergleichen (4)
•
Wie wird überhaupt die Abfrage ausgeführt?
•
EXPLAIN EXTENDED SELECT count(*)
FROM tbl_Musiker m,
tbl_MusikerInstrument mi,
tbl_Instrument i
WHERE i.Name = 'Gitarre'
AND
i.ID = mi.InstrumentID AND
m.ID = mi.MusikerID;
+-------+--------+-----------------+--------------------+------+-------------+
| table | type
| key
| ref
| rows | Extra
|
+-------+--------+-----------------+--------------------+------+-------------+
| i
| ALL
| NULL
| NULL
|
14 | Using where |
| mi
| ref
| FK_InstrumentID | GMCD2.i.ID
|
7 | Using index |
| m
| eq_ref | PRIMARY
| GMCD2.mi.MusikerID |
1 | Using index |
+-------+--------+-----------------+--------------------+------+-------------+
(Abgekürzte Darstellung)
RDB 7 - 27
Version 3.5
SQL-Optimierungen
MySQL Ausführungspläne vergleichen (5)
•
Wie wird überhaupt die Abfrage ausgeführt? Darstellung
mit mk-visual-explain (von unten an lesen)
JOIN
+- Unique index lookup
| key
m->PRIMARY
| possible_keys PRIMARY
| key_len
4
| ref
GMCD2.mi.MusikerID
| rows
1
+- JOIN
+- Index lookup
| key
mi->FK_InstrumentID
| possible_keys PRIMARY,FK_InstrumentID,FK_MusikerID
| key_len
4
| ref
GMCD2.i.ID
| rows
7
+- Filter with WHERE
+- Table scan
rows
14
+- Table
table
i
possible_keys PRIMARY
Quelle: http://www.maatkit.org/doc/mk-visual-explain.html
RDB 7 - 28
Version 3.5
SQL-Optimierungen
MySQL Ausführungspläne vergleichen (6)
•
Wir ändern die Reihenfolge der Elemente in der Abfrage
•
EXPLAIN EXTENDED SELECT count(*)
FROM tbl_Musiker m,
tbl_MusikerInstrument mi,
tbl_Instrument i
WHERE m.ID = mi.MusikerID
AND
i.ID = mi.InstrumentID AND
i.Name = 'Gitarre';
+-------+--------+-----------------+--------------------+------+-------------+
| table | type
| key
| ref
| rows | Extra
|
+-------+--------+-----------------+--------------------+------+-------------+
| i
| ALL
| NULL
| NULL
|
14 | Using where |
| mi
| ref
| FK_InstrumentID | GMCD2.i.ID
|
7 | Using index |
| m
| eq_ref | PRIMARY
| GMCD2.mi.MusikerID |
1 | Using index |
+-------+--------+-----------------+--------------------+------+-------------+
(Abgekürzte Darstellung)
Überhaupt keine Änderung im Plan... Wieso?
RDB 7 - 29
Version 3.5
SQL-Optimierungen
MySQL Ausführungspläne vergleichen (7)
•
Wir ändern die Reihenfolge der Elemente in der Abfrage
und schalten den JOIN-Optimierer aus...
•
EXPLAIN EXTENDED SELECT STRAIGHT_JOIN count(*)
FROM tbl_Musiker m,
tbl_MusikerInstrument mi,
tbl_Instrument i
WHERE m.ID = mi.MusikerID
AND
i.ID = mi.InstrumentID AND
i.Name = 'Gitarre';
ERROR 1064 (42000): You have an error in your SQL syntax
Wieso?
RDB 7 - 30
Version 3.5
SQL-Optimierungen
MySQL Ausführungspläne vergleichen (8)
•
•
Wir ändern die Reihenfolge der Elemente in der Abfrage,
schalten den Optimierer aus und schreiben einen richtigen JOIN
EXPLAIN EXTENDED SELECT STRAIGHT_JOIN count(*)
Neue
FROM tbl_Musiker m
Abfrage
INNER JOIN tbl_MusikerInstrument mi ON m.ID = mi.MusikerID
INNER JOIN tbl_Instrument i ON i.ID = mi.InstrumentID
WHERE i.Name = 'Gitarre';
+-------+--------+-----------------+--------------------+------+-------------+
| table | type
| key
| ref
| rows | Extra
|
+-------+--------+-----------------+--------------------+------+-------------+
| i
| ALL
| NULL
| NULL
|
14 | Using where |
| mi
| ref
| FK_InstrumentID | GMCD2.i.ID
|
7 | Using index |
| m
| eq_ref | PRIMARY
| GMCD2.mi.MusikerID |
1 | Using index |
+-------+--------+-----------------+--------------------+------+-------------+
+-------+--------+---------+-----------------------+------+-------------+
| table | type
| key
| ref
| rows | Extra
|
+-------+--------+---------+-----------------------+------+-------------+
| m
| index | PRIMARY | NULL
| 100 | Using index |
| mi
| ref
| PRIMARY | GMCD2.m.ID
|
1 | Using index |
| i
| eq_ref | PRIMARY | GMCD2.mi.InstrumentID |
1 | Using where |
+-------+--------+---------+-----------------------+------+-------------+
RDB 7 - 31
Version 3.5
SQL-Optimierungen
MySQL Ausführungspläne vergleichen (9)
•
Wir ändern die Reihenfolge der Elemente in der Abfrage,
schalten den Optimierer aus, schreiben einen richtigen JOIN und schalten den
Cache aus und führen die Abfrage aus:
•
SELECT STRAIGHT_JOIN SQL_NO_CACHE count(*)
FROM tbl_Musiker m
INNER JOIN tbl_MusikerInstrument mi ON m.ID = mi.MusikerID
INNER JOIN tbl_Instrument i ON i.ID = mi.InstrumentID
WHERE i.Name = 'Gitarre';
show status like "last_query_cost";
Faktor 7...
+-----------------+-----------+
| Variable_name
| Value
|
+-----------------+-----------+
| Last_query_cost | 31.799000 |
+-----------------+-----------+
+-----------------+-------+
| Variable_name
| Value |
+-----------------+-------+
| Last_query_cost | 221
|
+-----------------+-------+
alte Abfrage
neue (absichtlich)
nicht optimierte
Abfrage
RDB 7 - 32
Version 3.5
Ein Blick in die Zukunft
RDB vs. NoSQL (1)
The big 3
und ihr Problem
mit RDM in 2005
Apache Hadoop
MongoDB
CouchBase
URL-Indexing
Amazon
Facebook
MapReduce
HBase
Google
Document-DB
Neue DB-Typen
Speichern von
Verkaufsdaten
Social Network
Datenstruktur
BigData
The Players /
Problems /
New Comers
The Cloud
IoT (Internet of Things)
DynamoDB
Apache Cassandra
Consistency
KeyValue Store
CAP
Oracle NoSQL
Neo4J
Availability
Partition Tolerance
CAP vs. ACID
Graph-DB
Atomacity
ACID
Weitere
Störfaktoren
Consistency
Isolation
Durability
MongoDB
MEAN
Express (WebApp Framework)
AngularJS (JavaScript Framework)
Node.js (Computational Engine)
MEAN vs. LAMP
Linux
LAMP
Apache
MySQL
PHP
Quelle: Next Generation Databases, S. 15-73
RDB 7 - 33
Version 3.5
Ein Blick in die Zukunft
RDB vs. NoSQL (2)
Multi-record ACID
Single Object strict
Tunable/Eventual
Tables
Consistency
Schema
Eventual
Documents
Wide column
Graph
B-tree
Log-structured Merge Trees
Storage
Index free graph
Kriterien für die
Wahl einer DB
SQL
Processing
Columnar
Row bases
Format
Quelle: Next Generation Databases, S. 194
REST
Graph
MapReduce
RDB 7 - 34
Version 3.5
Übungen
1) Bringen Sie das Benerator-Beispiel auf Seite 15 zum Laufen
2) Probieren Sie diese SQL-Optimierungsbeispiele zu reproduzieren und
vor allem sie zu verstehen.
Parameter, die eine Rolle spielen:
1) Cache ein/aus
2) SQL JOIN oder nicht
3) JOIN-Reihenfolge forciert oder nicht
"EXPLAIN EXTENDED SELECT" und "last_query_cost" ausprobieren
3) Probieren Sie den Ausführungsplan eine Abfrage aus Ihrer eigenen
Fantasie/Erfahrung abzubilden, zu verstehen und am Schluss
von Hand zu optimieren.
RDB 7 - 35
Version 3.5
Herunterladen