Angewandte Kognitions- und Medienwissenschaft an der Universität Duisburg_Essen
Column Stores
1. Column Stores
Arno Schmidhauser
Letzte Revision: Januar 2011
2. Neues technisches Umfeld
• RAM Zugriff: 1 GB/sec Random (Seek Time 1-3 nsec, 3-6 Bus-
Cycles, 64 Byte Block), 10 GB/sec Maximum(Peak)
• Diskzugriff: 1 MB/sec Random (Seek Time 1 msec, 1 KB Block),
100 MB/sec Stream.
• Sehr performante Vektorverarbeitung in modernen CPU's mit
mehreren Cache-Levels.
• Das Absuchen eines Vektors mit 106 Datensätzen à 10 Bytes
dauert ca 0.01sec bei 1GB/sec, 0.001sec bei 10 GB/sec.
• Verfügbares Memory problemlos > 4 GB für Standard Server.
• Memory hat kleinere Random/Stream Problematik, Durchsatz
ist unabhängiger vom Ort der Daten im Ggs. zu Disk.
3. Neue Anfragetypen
Früher vorallem OLTP Heute zunehmend OLAP
select * from Bestellung select avg( betrag )
where id = 4711 from Bestellung
insert into Bestellung
values (…)
datensatzweise Abfragen spaltenweise Abfragen
4. Leseoperation überwiegen
• Frühe Annahme: Einfügen, Ändern, Löschen machen einen
relevanten Anteil der Datenbankoperationen aus.
• Neue Untersuchungen:
Selbst bei OLTP-Systemen
überwiegen heute die Anzahl
Leseoperationen deutlich.
5. Viele Spalten mit wenig Werten
• Sehr viele Spalten in Datenbanksystem enthalten
tatsächlich nur wenig verschiedene Werte.
• Beispiel: 29% aller Spalten (Attribute) enthalten lediglich
2 bis 32 verschiedene Werte.
6. Column Stores, wofür?
• Für viele Anwendungen ist die Architektur klassischer
Datenbanksysteme nicht optimal. Der Datensatz als logische
und technische Einheit ist nicht immer sinnvoll.
• Column Stores sind eine neue technologische Richtung. Im
Fokus steht dabei:
– Leseoperationen überwiegen gegenüber Änderungen.
– Abfragen überstreichen oft einen grossen Teil der
vorhanden Daten.
– Spaltenorientierte Abfragen überwiegen.
– Ganze Spalten oder ganze DB wird im Speicher gehalten.
– Vorallem numerische und kleine Datentypen, wenig
Strings.
7. Zeilenspeicherung
• Die klassische Datenbank-Technologie geht von OLTP-
Applikationen aus:
1. Datensatzorientierte Speicherung
2. Sequentielle Anhäufung von Datensätzen
3. Seitenorientierte Zusammenfassung von Datensätzen
Page 1 (Disk und Memory) Page 2 (Disk und Memory)
Datensatz 1 Date- Datensatz 9 Date-
satz 2 Datensatz 3 satz 10 Datensatz 11
Datensatz 4 Datensatz Datensatz 12 Datensatz
5 Datensatz 6 13 Datensatz 14
Datensatz 7 Datensatz 8 Datensatz 15 Datensatz 16
8. Spaltenspeicherung
Spalte C
Spalte A
Spalte B
• Wenn OLAP-Anfragen dominieren,
sind spaltenorientierte
Datensatz 1 w1 w1 w1
Speichertechnologien Datensatz 2 w2
effizienter: Datensatz 3 w3 w2
Datensatz 4 w4 w2
Datensatz 5 w5 w3
• Nur benötigte Spalten ins Memory laden, w4 w3
dafür Spalte in der ganzen Länge.
w5
• Effiziente Aggregatbildung. w4
• Zugehörigkeit eines Attributwertes
in einer Spalte zu einem Datensatz: w5
1. duch die Position (Array)
2. durch einen Schlüssel (ObjectId, RecordId)
9. Zeilen- und Spaltenspeicherung
Datensatzorientierte Spaltenorientierte
Speicherung Speicherung
Spalte C
Spalte D
Spalte N
Spalte A
Spalte B
Spalte E
Datensatz 1
Datensatz 2
OLTP
Datensatz 3
ineffizient
Datensatz 4
Datensatz n
effizient
Datensatz 1
Datensatz 2
OLAP
Datensatz 3
effizient
Datensatz 4
Datensatz n
ineffizient
10. Komprimierung
• Ein wesentlicher Vorteil von Column-Stores liegt darin,
dass Spalten oft eine hohe Anzahl gleicher Werte
enthalten.
• Komprimierung der Daten auf der Disk beschleunigt das
Laden und Speichern der Datenbank bei den Checkpoints.
• In der Regel heute im Memory dekomprimiert.
• Neueste Entwicklungen jedoch so, dass Daten auch im
Memory komprimiert abgelegt sind, und erst für die
Verarbeitung in der CPU (Level 2 Cache) dekomprimiert
werden.
11. Run Length Encoding
• Sehr effizient für sortierte Spalten, Beispiel:
Quantity Quantity
unkompr. (Wert;Anzahl)
1 1;5
1 3;2
1 10;5
1
1
3
3
10
10
10
10
10
12. Delta Encoding
• Variante 1: Numerische Werte werden als Differenz zum
kleinsten Wert codiert.
• Variante 2: Numerische Werte werden als Differenz zum
jeweils vorhergehenden Wert dargestellt.
13. Bit Vector Encoding
• für Spalten mit wenig Bit Vektor Werte
1000000 Montag
verschiedenen Werten, 0100000 Dienstag
z.B. Geschlecht, Wochentag, 0010000 Mittwoch
Land, Status usw. 0001000 Donnerstag
0000100 Freitag
0000010 Samstag
0000001 Sonntag
Tabelle mit Bit Vektor Werten
WochenTag Artikel ID Kunden ID Menge
0000001 43 4711 1.00
0000010 56 4711 2.00
0000100 43 6900 1.00
Bit Vektor Index
0000001 heisst: Datensatz 1 hat den Wert 0000001
0000010 heisst: Datensatz 2 hat den Wert 0000010
0000100 heisst: Datensatz 3 hat den Wert 0000100
14. Dictionary Encoding
• Lange und wiederholt vorkommende Werte werden in
einen Dictionary ausgelagert:
Dokument
URL Begriff ID
ti.bfh.ch Studium
ti.bfh.ch Bachelor
hkb.bfh.ch Studium
hkb.bfh.ch Kunst
Dokument Dictionary
URL dict ID dict ID Begriff
ti.bfh.ch 1 1 Studium
ti.bfh.ch 2 2 Bachelor
hkb.bfh.ch 1 3 Kunst
hkb.bfh.ch 3
• Wird z.B. auch bei der XML Speicherung für Element-
namen verwendet!
15. Indexierungsstrategie
• Bei klassischen Datenbanken werden alle Index auf Disk
gespeichert. Indexstrukturen können grösser als die
Datenbank selbst sein, oft sogar bis Faktor 10.
• Column Stores und In Memory Datenbank halten Indexe
nur im Memory. Indexe unterliegen keinem Recovery bei
einem System Crash, sondern werden neu gebaut.
• Indexzugriffe erfordern bei Column Stores nie Disk-I/O.
Indexe referenzieren nur Memory-Adressen oder
Arraypositionen in Spalten, jedoch keine externen
Datensatz ID's, Primary Keys o.ä.
16. Produktbeispiel MonetDB
• Open Source, Enge Zusammenarbeit mit Open Source
Datawarehouse Tools von Pentaho.
• SQL 93 kompatibel.
• ACID kompatibel, Snapshot-Isolation.
• Schnelles Erzeugen und Laden von Spalten-Files mit COPY
Utility.
• Extrem schnell bei spaltenorientierten Abfragen (Bis zu
Faktor 100 gegenüber mySQL).
• Performance bezüglich Einfügen, Ändern, Löschen mit SQL
ist kritisch, da bei Checkpoints ganze Spalten-Files
geschrieben werden.
• Kommerzielle Weiterentwicklung im Rahmen von Ingres
Vectorwise.
17. Tabellen, Aufspaltung in Spalten
• Originale Tabelle
• Pro Attribut eine
binäre Relation (BAT)
im Memory mit Objekt-
ID's und Attributwerten
• Binary Association Tables (BATs) sind die Grundstruktur
der Tabellenspeicherung in MonetDB (und anderen
ColumnStores)
18. Binary Association Table (BAT)
Hash Zugriff mit
OID als Schlüssel
Memory Mapped File (.tail)
Kompression auf Disk, um I/O zu
Werden beim
Laden erzeugt vermindern.
Kompressiosnsvarianten siehe weiter unten
19. Indexierung
• Beim ersten Zugriff auf ein Attribut wird eine Kopie seiner
BAT erstellt. Diese Kopie wird als Index nachgeführt und
schrittweise verfeinert, z.B. bei der ersten Abfrage wie
folgt:
select … from Discount where Discount > 0.1
Kopie
> 0.1
≤ 0.1
20. Einfaches Query
• Abfrage:
select * from Discount where Discount > 0.1
• Ausführung:
1. Sequentielles Scannen der Discount-BAT: alle OID's
suchen, für die Discount > 0.1.
2. Mit den OID's in den übrigen BATs die anderen
Attribute des Datensatzes suchen.
21. Aggregat Query
• Abfrage:
select Day, avg(Discount) from Discount
where Discount = 0.065 and Day = '9/4/98'
• Ausführung ohne Index:
1. Spalten sequentiell scannen und je einen Bit-Vektor
bilden: 1 = Bedingung erfüllt, 0 = Bedingung nicht erfüllt.
2. and-Verknüpfung der beiden Bit-Vektoren durchführen.
3. Korrespondierende Array-Positionen in der Day- und
Discount-BAT entnehmen, avg() bilden und ausgeben.
22. Aggregat Query
select Day, avg(Discount) from Discount
where Discount = 0.065 and Day = '9/4/98'
0
1
0
0
0
0
1
AND 0
0
0
0
1
0
0
1
23. Aggregat Query mit Joins
• Beispiel einer Join-Abfrage
select o.year, sum(o.quantity)
from Order o, Customer c, Product p
where o.custID = c.custID and o.prodID = p.prodID
and p.name = 'P1'
and c.region = 'europe'
group by o.year
• Im folgenden soll die spaltenorientierte Verarbeitung dieser
Abfrage gezeigt werden.
24. Join, Tabellen
Customer Product
custID name region prodID name
1 C1 europe 1 P1
2 C2 europe 2 P2
3 C3 asia 3 P3
4 C4 asia 4 P4
5 C5 pacific 5 P5
Order
custID prodID year quantity
1 1 2009 10
1 5 2010 10
2 1 2009 1000
3 1 2009 500
3 4 2009 500
3 2 2010 500
2 1 2007 100
25. Join, Spalten
• Achtung: Die Datenbank besteht physisch aus einer
Sammlung von Spalten (BATs in MonetDB)
BAT BAT BAT BAT BAT
OID c.custID OID c.name OID c.region OID p.prodID OID p.name
101 1 101 C1 101 europe 201 1 201 P1
102 2 102 C2 102 europe 202 2 202 P2
103 3 103 C3 103 asia 203 3 203 P3
104 4 104 C4 104 asia 204 4 204 P4
105 5 105 C5 105 pacific 205 5 205 P5
BAT BAT BAT BAT
OID o.custID OID o.prodID OID o.year OID o.quantity
1001 1 1001 1 1001 2009 1001 10
1002 1 1002 5 1002 2010 1002 10
1003 2 1003 1 1003 2009 1003 1000
1004 3 1004 1 1004 2009 1004 500
1005 3 1005 4 1005 2009 1005 500
1006 3 1006 2 1006 2010 1006 500
1007 2 1007 1 1007 2007 1007 100
26. Join, Ablauf, Schritt 1 und 2
c.region = 'europe'
BV1 TB1
OID c.region Bit OID c.custID custID
101
102
europe
europe
1
1
101
102
1
2
1
2
103 asia 0 103 3
104 asia 0 104 4
105 pacific 0 105 5
p.name = 'P1'
BV2 TB2
OID p.name Bit OID p.prodID prodID
201
202
P1
P2
1
0
201
202
1
2
1
203 P3 0 203 3
204 P4 0 204 4
205 P5 0 205 5
27. Join, Ablauf Schritt 1 und 2
Operation Algorithmus Resultat
c.region = 'europe' linearer Scan über c.region (Verbesserung BV1: Bit Vektor, ermittelt über c.region,
auswerten für Stringdaten:Indexzugriff, Dictionary welcher pro Position in der Customer
Lookup, Komprimierte Daten usw.) Tabelle angibt, ob der Datensatz die
Bedingung erfüllt.
BV1 auf c.custID anwenden Scan über BV1 und c.custID, jeweils in TB1: Temoräre BAT mit allen custID.
beiden Vektoren Position für Position CustID als Hashkey
vorrücken, da beide gleichviele Einträge
haben. Alle custID entnehmen, welche an
entsprechender Positione im Bit Vektor eine
1 haben.
p.name = 'P1' auswerten linearer Scan über p.name (Verbesserung BV2: Bit Vektor, ermittelt über c.region,
für Stringdaten:Indexzugriff, Dictionary welcher pro Position in der Product
Lookup, Komprimierte Daten usw.) Tabelle angibt, ob der Datensatz die
Bedingung erfüllt
BV2 auf p.prodID anwenden Scan über BV2 und p.prodtID, jeweils in TB1: Temoräre BAT mit allen prodID.
beiden Vektoren Position für Position ProdID als Hashkey
vorrücken, da beide gleichviele Einträge
haben. Alle prodID entnehmen, welche an
entsprechender Positione im Bit Vektor eine
1 haben.
28. Join-Ablauf, Schritt 3 bis 7
TB1 1 Scan über o.custID mit Lookup in TB1. Wenn
custID 2
BV3
custID in TB1 vorkommt, dann in BV3 1
eintragen.
OID o.custID Bit
1001 1 1
1002 1 1
1003 2 1
1004 3 0
1005 3 0 BV4
1006 3 0 Bit OID o.year OID o.quantity
1007 2 1 1 1001 2009 1001 10
0
1
1002 2010
1003 2009
1002 10
1003 1000
AND Verknüpfung 0 1004 2009 1004 500
TB2 1 0 1005 2009 1005 500
prodID 0 1006 2010 1006 500
1 1007 2007 1007 100
Scan über o.prodID mit Lookup in
TB2. Wenn custID in TB2 Aus den BATs o.year und o.quantity Zeilen
vorkommt, dann in BV4 1
gemäss Bit Vektor BV4 entnehmen.
BV4
OID o.prodID Bit OID o.year o.quantity
1001 1 1 1001 2009 10
1002 5 0 1003 2009 1000
1003 1 1 1007 2007 100
1004 1 1
1005 4 0
1006 2 0 Gruppiert = Sortiert o.year o.quantity
1007 1 1 und zusammen- 2009 1010
gefasst, z.B. über
Merge-Sort
2007 100
29. Join-Ablauf, Schritt 3 bis 7
c.region = 'europe' auswerten linearer Scan über c.region (Verbesserung für BV1: Bit Vektor, ermittelt über c.region, welcher pro
Stringdaten:Indexzugriff, Dictionary Lookup, Komprimierte
Daten usw.)
Position in der Customer Tabelle angibt, ob der Datensatz
die Bedingung erfüllt.
BV1 auf c.custID anwenden Scan über BV1 und c.custID, jeweils in beiden Vektoren TB1: Temoräre BAT mit allen custID. CustID als Hashkey
Position für Position vorrücken, da beide gleichviele
Einträge haben. Alle custID entnehmen, welche an
entsprechender Positione im Bit Vektor eine 1 haben.
p.name = 'P1' auswerten linearer Scan über p.name (Verbesserung für BV2: Bit Vektor, ermittelt über c.region, welcher pro
Stringdaten:Indexzugriff, Dictionary Lookup, Komprimierte
Daten usw.)
Position in der Product Tabelle angibt, ob der Datensatz
die Bedingung erfüllt
BV2 auf p.prodID anwenden Scan über BV2 und p.prodtID, jeweils in beiden Vektoren TB1: Temoräre BAT mit allen prodID. ProdID als Hashkey
Position für Position vorrücken, da beide gleichviele
Einträge haben. Alle prodID entnehmen, welche an
entsprechender Positione im Bit Vektor eine 1 haben.
Vergleich o.custID mit Einträgen in TB1 Scan über o.custID, lookup jeder custID in TB1 BV3: Bit Vektor, welcher pro Eintrag in o.custID angibt, ob
die entsprechende custID in TB1 vorkommt.
Vergleich o.prodID mit Einträgen in TB2 Scan über o.prodID, lookup jeder custID in TB2 BV4: Bit Vektor, welcher pro Eintrag in o.custID angibt, ob
die entsprechende custID in TB2 vorkommt.
AND Verknüpfung auswerten AND Verknüpfung von BV3 und BV4 BV5: Bit Vektor, welcher pro Eintrag für Tabelle Order
angibt, ob er den Bedingungen c.region = 'europe' und
p.name = 'P1' genügt.
BV5 auf o.year anwenden linearer Scan über BV5 und o.year TB3: Temporäre BAT mit year
•
BV5 auf o.quantity anwenden linearer Scan über BV5 und o.quantity TB4: Temporäre BAT mit quantity
Algorithmus zum Gruppieren anwenden
Verbund von TB3 und TB4 TB5: Temporäre BAT mit 2 Attributen (year, quantity)
TB5 sortieren nach year, Irgendeine Gruppier-Algorithmus, z.B. Sortieralgorithmus, Ausgabe von year und sum(quantity)
summieren von quantity z.B. Merge-Sort, dann zusammenfassen.
30. Produktbeispiel Ingres Vectorwise
• kommerzielles Produkt. 64 Bit auf Windows, Linux und als
Amazon Webservice (Amazon Machine Image).
• Datensätze, resp. Werte in den Spalten werden in
Einfügereihenfolge gespeichert (RAW Mode).
• Parameter block_size definiert Blockgrösse, die für das
Ablegen von Spalten benützt werden. Ein Block ist die I/O
Einheit zur Disk. Jeder Block beinhaltet Statistikangaben, z.B.
grösster und kleinster Spaltenwert.
• Blöcke werden komprimiert. Datenbank bestimmt automatisch
Algorithmus pro Block: Run Length Encoding, Dictionary
Encoding und Delta Encoding.
31. Produktbeispiel Ingres Vectorwise
• Von jedem Datenblock einer Spalte wird der Minimum und
Maximumwert im Memory gehalten ("Index" ).
• Jede Tabellen kann einen Clustered Index haben (create index
Befehl). Keine anderen Indexe sind möglich.
• Indexstrukturen werden ausschliesslich im Memory gehalten
(Bei Crash zerstört).
• Änderungen werden über Positional Delta Trees verwaltet. Bei
einem bestimmtem Umfang an Änderungen wird die gesamte
DB auf Disk propagiert (Checkpoint).
32. Änderungen in Column Stores
• Einfügen/Ändern/Löschen in Column Stores werden als
"notwendiges Übel" angesehen, im Gegensatz zur
klassischen DB-Technologie.
Änderungen über eine bestimmte Zeit hinweg in einer
"Delta-Struktur" Hauptstruktur Delta-Struktur Nach
sammeln. Disk/Memory (Änderungen) Checkpoint
Spalte/Datensatz
Bei Checkpoint
anwenden auf A a einfügen a
Hauptstruktur B
C
a einfügen
a einfügen
a
a
der Datenbank. D d einfügen A
E B ändern auf b b
F C löschen d
D
E
F
33. Positional Delta Tree, PDT
• Der Positional Delta Tree ist eine hocheffiziente Delta-
Struktur, damit beim Lesen von Spalten auch Änderungen
miteinbezogen werden.
• Ausgangslage:
– Spalten (oder Datensätze) liegen in einer bestimmten
Sortierung vor, die bei Abfragen verwendet wird.
– Die Sortierung von Spalten und Datensätzen muss
daher zusammen mit der Delta-Struktur jederzeit
rekonstruiert werden können.
– Die Delta-Struktur darf Abfragen nur minimal
verlangsamen.
34. PDT-Beispiel, Tabelle
• Die SID ist die Position des Datensatzes, respektive der
Spaltenwerte, auf Disk oder im Memory, wenn noch keine
Änderungen stattgefunden haben.
Sortier-Schlüssel, physische
Reihenfolge
35. PDT-Beispiel, Änderungsbefehle
• Folgende Änderungen sollen nun vorgenommen werden:
1. INSERT INTO inventory VALUES ('Berlin','table','Y',10)
2. INSERT INTO inventory VALUES ('Berlin','cloth','Y',5)
3. INSERT INTO inventory VALUES ('Berlin','chair','Y',20)
4. INSERT INTO inventory VALUES ('Paris','sofa','Y',3)
5. UPDATE inventory SET qty=13 WHERE store='London' AND
product='stool'
6. DELETE FROM inventory WHERE store='London' AND product='table'
38. Mehrschichtige Deltastruktur
• Delta-Strukturen können auch zur Realisierung von
Snapshot-Isolation verwendet werden.
• Für jede neue Transaktion: Datenbankweite Delta-Struktur
für die Transaktion kopieren, und mit Zeitstempel resp.
Versionennummer der Transaktion versehen.
• Transaktion arbeitet mit der kopierten Delta-Struktur lokal
für sich weiter.
• Beim Commit: lokale Delta-Struktur der Datenbank
übergeben. Diese gleicht mit der datenbankweiten Delta-
Struktur ab (Siehe Versionenverfahren für Concurrency
Control).
39. Spaltenspeicherung in klassischen DB's
• Ideen der Spaltenspeicherung zur Abfrage-Beschleunigung
existieren bereits in den klassischen Datenbanksystemen.
Beispiel mit MSSQL 2008:
create index on Bestellung ( bestDatum )
include ( menge )
Das Attribut menge wird damit in den Blattknoten des
Index abgelegt. So wird für folgende Abfragen der Zugriff
auf die Haupttabelle überflüssig:
select bestDatum, sum( menge )
from Bestellung
group by bestDatum
40. Spaltenspeicherung in klassischen DB's
• Pros:
– Perfekt integriert in bestehende DB-Lösung.
– 100% unsichtbar gegenüber Applikationen und
Clientsystemen.
– Macht das Laden der Basistabelle überflüssig, wenn die
Abfrage aus allen Attributen des Index befriedigt
werden kann.
• Cons:
– Klassische I/O Technologie: der Index muss zuerst
geladen werden. I/O Pages sind nicht immer garantiert
im Memory.
– Index kann andere Daten aus dem Memory
verdrängen, da er zusätzlich Platz benötigt.
41. Zusammenfassung Column Stores
• Für grosse Datenbanken mit Schwergewicht OLAP Abfragen.
• Abfrage-Performance kann sich um Faktor 100 verbessern,
bei vergleichbarer Hardware.
• Schlechtere Update-Performance.
• Gute Bulk-Load Performance, da einfache Datenstrukturen
(wenig Indexe).
Eignung als Datawarehouse oder Data Mart.
ETL Column Store Load
Data Mart
Quelldaten
Quelldaten DWH
Quelldaten
Data Mart