1914 lines
		
	
	
		
			98 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			1914 lines
		
	
	
		
			98 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
# Motivation und Grundlagen
 | 
						||
## Aufgaben und Komponenten eines DBMS
 | 
						||
Prinzipien: Die neun Codd’schen Regeln
 | 
						||
1. Integration: einheitliche, nichtredundante Datenverwaltung
 | 
						||
2. Operationen: Speichern, Suchen, Ändern
 | 
						||
3. Katalog: Zugriffe auf Datenbankbeschreibungen im Data Dictionary
 | 
						||
4. Benutzersichten
 | 
						||
5. Integritätssicherung: Korrektheit des Datenbankinhalts
 | 
						||
6. Datenschutz: Ausschluss unauthorisierter Zugriffe
 | 
						||
7. Transaktionen: mehrere DB-Operationen als Funktionseinheit
 | 
						||
8. Synchronisation: parallele Transaktionen koordinieren
 | 
						||
9. Datensicherung: Wiederherstellung von Daten nach Systemfehlern
 | 
						||
 | 
						||
Betrachtete Fragestellung
 | 
						||

 | 
						||
 | 
						||
**Zentrale Komponenten**
 | 
						||
- **Anfrageverarbeitung** : Planung, Optimierung und Ausführung deklarativer Anfragen
 | 
						||
- **Transaktionsverwaltung** : Koordination und Synchronisation von Transaktionen, Durchführung von Änderungen, Sicherung der ACID-Eigenschaften
 | 
						||
- **Speichersystem** : Organisation der Daten im Hauptspeicher und auf dem Externspeicher für effizienten Zugriff und Persistenz
 | 
						||
 | 
						||
## Relationale vs. nicht-relationale DBMS
 | 
						||
**Relationale DBMS**
 | 
						||
- Basis: **Relationenmodell** = Daten in Tabellen strukturiert
 | 
						||
- Beziehungen über Werte (= Fremdschlüssel),
 | 
						||
    Integritätsbedingungen
 | 
						||
- **SQL** als standardisierte Anfragesprache
 | 
						||
- kommerziell erfolgreichstes Datenmodell: Oracle, IBM DB2,
 | 
						||
    MS SQL Server, SAP HANA, ...
 | 
						||
 | 
						||
| WEINE | WeinID            | Name | Farbe | Jahrgang    | Weingut |
 | 
						||
| ----- | ----------------- | ---- | ----- | ----------- | ------- |
 | 
						||
| 1042  | La Rose Grand Cru | Rot  | 1998  | Château ... |
 | 
						||
| 2168  | Creek Shiraz      | Rot  | 2003  | Creek       |
 | 
						||
| 3456  | Zinfandel         | Rot  | 2004  | Helena      |
 | 
						||
| 2171  | Pinot Noir        | Rot  | 2001  | Creek       |
 | 
						||
| 3478  | Pinot Noir        | Rot  | 1999  | Helena      |
 | 
						||
| 4711  | Riesling Reserve  | Weiß | 1999  | Müller      |
 | 
						||
| 4961  | Chardonnay        | Weiß | 2002  | Bighorn     |
 | 
						||
 | 
						||
**Kritik an RDBMS / SQL**
 | 
						||
- nicht skalierbar
 | 
						||
    - Normalisierung von Relationen, viele Integritätsbedingungen zu prüfen
 | 
						||
    - kann man in RDBMS auch vermeiden!
 | 
						||
- starre Tabellen nicht flexibel genug
 | 
						||
    - schwach typisierte Tabellen (Tupel weichen in den tatsächlich genutzten Attributen ab)
 | 
						||
          - viele Nullwerte wenn alle potentiellen Attribute definiert
 | 
						||
          - alternativ Aufspaltung auf viele Tabellen
 | 
						||
          - Schema-Evolution mit **alter table** unflexibel
 | 
						||
    - tatsächlich in vielen Anwendungen ein Problem
 | 
						||
- Integration von spezifischen Operationen (Graphtraversierung, Datenanalyse-Primitive) mit Stored Procedures zwar möglich führt aber oft zu schwer interpretierbarem Code
 | 
						||
 | 
						||
**NoSQL-Systeme**
 | 
						||
- Datenmodelle
 | 
						||
    - KV-Stores
 | 
						||
    - Wide Column Stores
 | 
						||
    - Dokumenten-orientierte Datenhaltung
 | 
						||
    - Graph-Speicher
 | 
						||
    - ...
 | 
						||
- Anfragesprache -> unterschiedliche Ansätze:
 | 
						||
    - einfache funktionale API
 | 
						||
    - Programmiermodell für parallele Funktionen
 | 
						||
    - angelehnt an SQL-Syntax
 | 
						||
    - ...
 | 
						||
- Beispiele
 | 
						||
  - dokumentenorientierte Datenbanksysteme: MongoDB
 | 
						||
      - semistrukturierte Dokumente in JSON- bzw. BSON-Format
 | 
						||
      - Anfragen: CRUD erweitert um dokumentspezifische Suche
 | 
						||
  - Graph-Datenbanksysteme: Neo4j
 | 
						||
      - Property Graphen als Datenmodell: Knoten und Kanten mit Eigenschaften
 | 
						||
      - Anfragesprache Cypher
 | 
						||
      - Muster der Form "Knoten -> Kante -> Knoten ..."
 | 
						||
 | 
						||
## OLTP, OLAP und HTAP
 | 
						||
### OLTP vs OLAP
 | 
						||
|                            | Online Transactional Processing (OLTP)                                            | Online Analytical Processing (OLAP)                          |
 | 
						||
| -------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------ |
 | 
						||
|                            | -> Klassische operative Informationssysteme                                       | -> Data Warehouse                                            |
 | 
						||
|                            | Erfassung und Verwaltung von Daten                                                | Analyse im Mittelpunkt = entscheidungsunterstützende Systeme |
 | 
						||
|                            | Verarbeitung unter Verantwortung der jeweiligen Abteilung                         | Langandauernde Lesetransaktionen auf vielen Datensätzen      |
 | 
						||
|                            | Transaktionale Verarbeitung: kurze Lese-/ Schreibzugriffe auf wenigen Datensätzen | Integration, Konsolidierung und Aggregation der Daten        |
 | 
						||
|                            | ACID-Eigenschaften                                                                |                                                              |
 | 
						||
|                            |                                                                                   |
 | 
						||
| **Anfragen**               |                                                                                   |
 | 
						||
| Fokus                      | Lesen, Schreiben, Modifizieren, Löschen                                           | Lesen, periodisches Hinzufügen                               |
 | 
						||
| Transaktionsdauer und -typ | kurze Lese- / Schreibtransaktionen                                                | langandauernde Lesetransaktionen                             |
 | 
						||
| Anfragestruktur            | einfach strukturiert                                                              | komplex                                                      |
 | 
						||
| Datenvolumen einer Anfrage | wenige Datensätze                                                                 | viele Datensätze                                             |
 | 
						||
| Datenmodell                | anfrageflexibel                                                                   | analysebezogen                                               |
 | 
						||
| Antwortzeit                | msecs ...secs                                                                     | secs ...min                                                  |
 | 
						||
|                            |                                                                                   |
 | 
						||
| **Daten**                  |                                                                                   |
 | 
						||
| Datenquellen               | meist eine                                                                        | mehrere                                                      |
 | 
						||
| Eigenschaften              | nicht abgeleitet, zeitaktuell, autonom, dynamisch                                 | abgeleitet / konsolidiert, historisiert, integriert, stabil  |
 | 
						||
| Datenvolumen               | MByte ...GByte                                                                    | GByte ...TByte ...PByte                                      |
 | 
						||
| Zugriffe                   | Einzeltupelzugriff                                                                | Tabellenzugriff (spaltenweise)                               |
 | 
						||
 | 
						||
**OLTP: Beispiel**
 | 
						||
```sql
 | 
						||
BEGIN ;
 | 
						||
SELECT KundenNr INTO KNr
 | 
						||
FROM Kunden WHERE email = '...';
 | 
						||
INSERT INTO BESTELLUNG VALUES (KNr, BestNr, 1);
 | 
						||
UPDATE Artikel SET Bestand = Bestand-1
 | 
						||
WHERE ArtNr = BestNr;
 | 
						||
COMMIT TRANSACTION ;
 | 
						||
```
 | 
						||
 | 
						||
**OLAP: Beispiel**
 | 
						||
```sql
 | 
						||
SELECT DISTINCT ROW Zeit.Dimension AS Jahr,
 | 
						||
    Produkt.Dimension AS Artikel,
 | 
						||
    AVG(Fact.Umsatz) AS Umsatzdurchschnitt,
 | 
						||
    Ort.Dimension AS Verkaufsgebiet
 | 
						||
FROM (Produktgruppe INNER JOIN Produkt ON Produktgruppe.
 | 
						||
    [Gruppen-Nr] = Produkt.[Gruppen-ID]) INNER JOIN
 | 
						||
    ((((Produkt INNER JOIN [Fact.Umsatz] ON Produkt.[Artikel-Nr]
 | 
						||
    = [Fact.Umsatz].[Artikel-Nr]) INNER JOIN Order ON
 | 
						||
    [Fact.Umsatz].[Bestell-Nr]= Order.[Order-ID]) INNER JOIN
 | 
						||
    Zeit.Dimension ON Orders.[Order-ID] =
 | 
						||
    Zeit.Dimension.[Order-ID]) INNER JOIN Ort.Dimension ON
 | 
						||
    Order.[Order-ID] = Ort.Dimension.[Order-ID]) ON
 | 
						||
    Produktgruppe.[Gruppen-Nr] = Produkt.[Gruppen-ID]
 | 
						||
GROUP BY Produkt.Dimension.Gruppenname, Ort.Dimension.Bundesland,
 | 
						||
Zeit.Dimension.Jahr;
 | 
						||
```
 | 
						||
 | 
						||
### HTAP
 | 
						||
- HTAP = Hybrid Transactional and Analytics Processing
 | 
						||
- Ziel: schnellere Geschäftsentscheidungen durch "Echtzeit"-Verarbeitung
 | 
						||
- OLAP und OLTP auf der gleichen Datenbank: naheliegend aber große technische Herausforderung
 | 
						||
    - sehr unterschiedliche Workloads (Anfrage- und Lastprofile)
 | 
						||
    - Transaktionsverwaltung: gegenseitige Beeinflussung von Änderungs- und Leseoperationen reduzieren
 | 
						||
    - unterschiedliche Datenorganisation (physisch, logisch)
 | 
						||
- Herausforderungen
 | 
						||
  - Analytical (OLAP) und Transactional processing (OLTP)
 | 
						||
    - verschiedene Zugriffscharakterisiken
 | 
						||
    - verschiedene Performance-Ziele (Latenz vs. Durchsatz)
 | 
						||
  - => Unterschiedliche Optimierungen notwendig
 | 
						||
 | 
						||
## Disk- vs. Main-Memory-Systeme**
 | 
						||
**Traditionelle Annahmen**
 | 
						||
- Daten sollen dauerhauft aufbewahrt werden
 | 
						||
- Datenbank >> Hauptspeicher
 | 
						||
- Disk >> Hauptspeicher
 | 
						||
- Hauptspeicher = flüchtiger (volatiler) Speicher
 | 
						||
- Disk-IO dominiert Kosten
 | 
						||
 | 
						||
**Speicherhierarchie**
 | 
						||

 | 
						||
 | 
						||
**Eigenschaften von Speichermedien**
 | 
						||
|                 | Primär   | Sekundär  | Tertiär      |
 | 
						||
| --------------- | -------- | --------- | ------------ |
 | 
						||
| Geschwindigkeit | schnell  | langsam   | sehr langsam |
 | 
						||
| Preis           | teuer    | preiswert | billig       |
 | 
						||
| Stabilität      | flüchtig | stabil    | stabil       |
 | 
						||
| Größe           | klein    | groß      | sehr groß    |
 | 
						||
| Granulate       | fein     | grob      | grob         |
 | 
						||
 | 
						||
**Speichermedien**
 | 
						||
- **Primärspeicher**
 | 
						||
    - Primärspeicher: Cache und Hauptspeicher
 | 
						||
    - sehr schnell, Zugriff auf Daten fein granular: theoretisch jedes Byte adressierbar (Cachelines)
 | 
						||
- **Sekundärspeicher**
 | 
						||
    - Sekundärspeicher oder Online-Speicher
 | 
						||
    - meist Plattenspeicher, nicht-flüchtig
 | 
						||
    - Granularität des Zugriffs gröber: Blöcke, oft 512 Bytes
 | 
						||
    - Zugriffslücke: Faktor 10^5 langsamerer Zugriff
 | 
						||
- **Tertiärspeicher**
 | 
						||
    - Zur langfristigen Datensicherung (Archivierung) oder kurzfristigen Protokollierung (Journale)
 | 
						||
    - üblich: optische Platten, Magnetbänder
 | 
						||
    - "Offline-Speicher" meist Wechselmedium
 | 
						||
    - Nachteil: Zugriffslücke extrem groß
 | 
						||
 | 
						||
 | 
						||
**Transferraten HDD vs. SSD**
 | 
						||

 | 
						||
 | 
						||
**Konsequenz für disk-basierte Systeme**
 | 
						||
- blockbasierter Zugriff mit typischen Blockgrößen ≥ 4 KB
 | 
						||
- speziell für Magnetplatten Optimierung auf sequentielle Zugriffe
 | 
						||
       - Disklayout: Organisation der Daten auf der Disk = fortlaufende Folge von Blöcken
 | 
						||
       - sequentielles Lesen und Schreiben
 | 
						||
- Zugriffslücke zwischen Hauptspeicher und Disk durch Caching verbergen (Lokalität von Zugriffen ausnutzen)
 | 
						||
 | 
						||
**Main-Memory-Datenbanken**
 | 
						||
- klassische Annahmen nicht mehr zutreffend:
 | 
						||
    - Systeme mit Hauptspeicher im TB-Bereich verfügbar
 | 
						||
    - Datenbank kann komplett im Hauptspeicher gehalten werden (muss aber dennoch persistent sein)
 | 
						||
- **Main-Memory-** oder **Hauptspeicher-** Datenbanken: Ausnutzung der großen Hauptspeicher und Multicore-Architekturen
 | 
						||
       - Beispiele: SAP HANA, Oracle TimesTen, SQL Server Hekaton, Hyper, MemSQL, ...
 | 
						||
       - Besonderheiten: hauptspeicheroptimierte Datenstrukturen (Main-Memory-Scans), Persistenz trotz volatilem Speicher, Datenkompression, Nebenläufigkeitskontrolle
 | 
						||
 | 
						||
 | 
						||
## Klassische 5-Schichtenarchitektur
 | 
						||
**Fünf-Schichtenarchitektur**
 | 
						||
- Architektur für klassische DBMS
 | 
						||
- basierend auf Idee von Senko 1973
 | 
						||
- Weiterentwicklung von Härder 1987
 | 
						||
- Umsetzung im Rahmen des IBM-Prototyps _System R_
 | 
						||
- genauere Beschreibung der Transformationskomponenten
 | 
						||
    - schrittweise Transformation von Anfragen/Änderungen bis hin zu Zugriffen auf Speichermedien
 | 
						||
    - Definition der Schnittstellen zwischen Komponenten
 | 
						||
 | 
						||
**5-Schichtenarchitektur: Funktionen**
 | 
						||

 | 
						||
 | 
						||
**5-Schichtenarchitektur: Objekte**
 | 
						||

 | 
						||
 | 
						||
Erläuterungen
 | 
						||
- mengenorientierte Schnittstelle **MOS** :
 | 
						||
    - deklarative Datenmanipulationssprache auf Tabellen und Sichten (etwa SQL)
 | 
						||
- durch Datensystem auf satzorientierte Schnittstelle **SOS** umgesetzt:
 | 
						||
       - navigierender Zugriff auf interner Darstellung der Relationen
 | 
						||
       - manipulierte Objekte: typisierte Datensätze und interne Relationen sowie logische Zugriffspfade (Indexe)
 | 
						||
       - Aufgaben des Datensystems: Übersetzung und Optimierung von SQL-Anfragen
 | 
						||
- durch Zugriffssystem auf interne Satzschnittstelle **ISS** umgesetzt:
 | 
						||
       - interne Tupel einheitlich verwalten, ohne Typisierung
 | 
						||
       - Speicherstrukturen der Zugriffspfade (konkrete Operationen auf B+-Bäumen und Hashtabellen), Mehrbenutzerbetrieb mit Transaktionen
 | 
						||
- durch Speichersystem Datenstrukturen und Operationen der ISS auf interne Seiten eines virtuellen linearen Adressraums umsetzen
 | 
						||
       - Manipulation des Adressraums durch Operationen der Systempufferschnittstelle **SPS**
 | 
						||
       - Typische Objekte: interne Seiten, Seitenadressen
 | 
						||
       - Typische Operationen: Freigeben und Bereitstellen von Seiten, Seitenwechselstrategien, Sperrverwaltung, Schreiben des Logs
 | 
						||
- durch Pufferverwaltung interne Seiten auf Blöcke der Dateischnittstelle **DS** abbilden
 | 
						||
       - Umsetzung der DS-Operationen auf Geräteschnittstelle erfolgt durch BS
 | 
						||
 | 
						||
## Neue Entwicklungen
 | 
						||
Anforderungen aus neuen Anwendungen
 | 
						||
- Nicht-Standard-Datenmodelle (siehe NoSQL-Systeme)
 | 
						||
- flexibler Umgang mit Datenstrukturen (JSON, Schema on Read, ...)
 | 
						||
- beschränkte (Lookups) vs. erweiterte (z.B. Graphoperationen, Datenanalysen) Anfragefunktionalität
 | 
						||
- Skalierbarkeit zu Big Data (massiv parallele/verteilte Systeme)
 | 
						||
- dynamische Daten / Datenströme
 | 
						||
- ...
 | 
						||
 | 
						||
**Entwicklungen im Hardware-Bereich**
 | 
						||
- Multicore- und Manycore-Prozessoren: 64+ Cores
 | 
						||
    - Nutzung erfordert Parallelisierungstechniken und Nebenläufigkeitskontrolle
 | 
						||
- Memory Wall: Hauptspeicherzugriff als Flaschenhals
 | 
						||
    - RAM-Zugriff 60 ns, L1-Cache: 4 CPU-Zyklen -> Cache-optimierte Strukturen
 | 
						||
- Datenbank-Accelerators
 | 
						||
    - Hardware-unterstütztes Datenmanagement: FPGA, GPU als Coprozessoren, Highspeed-Netzwerk, SSDs als zusätzliche Cache-Ebene, ...
 | 
						||
- Persistenter Memory: nicht-volatiler Speicher
 | 
						||
    - Instant Restart / Recovery von Main-Memory-Datenbanken
 | 
						||
 | 
						||
**Zusammenfassung**
 | 
						||
- Datenmanagementfunktionalitäten in vielen Softwaresystemen erforderlich
 | 
						||
- nicht auf Implementierung kompletter DBMS beschränkt, sondern für nahezu alle datenintensiven Systeme: auch in Suchmaschinen, Datenanalyseanwendungen, eingebetteten Systemen, Visualisierungssystemen, Steuerungssystemen, Entwicklungsumgebungen, ...
 | 
						||
- gemeinsame Aufgaben / Komponenten: Datenorganisation und -verwaltung (Indexstrukturen), Transaktionsverwaltung / Nebenläufigkeitskontrolle / Recovery, Anfrageverarbeitung
 | 
						||
- betrifft Datenstrukturen und Algorithmen
 | 
						||
 | 
						||
# Speicherstrukturen für Datenbanken
 | 
						||
## Speicher- und Sicherungsmedien
 | 
						||
Speichermedien
 | 
						||
- verschiedene Zwecke:
 | 
						||
    - Daten zur Verarbeitung bereitstellen
 | 
						||
    - Daten langfristig speichern (und trotzdem schnell verfügbar halten)
 | 
						||
    - Daten sehr langfristig und preiswert archivieren unter Inkaufnahme etwas längerer Zugriffszeiten
 | 
						||
- Speicherhierarchie:
 | 
						||
    1. Extrem schneller Prozessor mit Registern
 | 
						||
    2. Sehr schneller Cache-Speicher
 | 
						||
    3. Schneller Hauptspeicher
 | 
						||
    4. Langsamer Sekundärspeicher mit wahlfreiem Zugriff
 | 
						||
    5. Sehr langsamer Nearline-Tertiärspeicher bei dem die Speichermedien automatisch bereitgestellt werden
 | 
						||
    6. Extrem langsamer Offline-Tertiärspeicher, bei dem die Speichermedien per Hand bereitgestellt werden
 | 
						||
 | 
						||
Zugriffslücke in Zahlen
 | 
						||
- Zugriffslücke: Unterschiede in den Zugriffsgeschwindigkeiten auf den verschiedenen Speicherebenen
 | 
						||
 | 
						||
| Speicherart              | Zugriffszeit | CPU cycles | typische Kapazität         |
 | 
						||
| ------------------------ | ------------ | ---------- | -------------------------- |
 | 
						||
| CacheSpeicher            | 6 ns         | 12         | 256 KB (L2) bis 32 MB (L3) |
 | 
						||
| Hauptspeicher            | 60 ns        | 120        | 1 GB bis 1.5 TB            |
 | 
						||
| Zugriffslücke $10^5$     |              |            |
 | 
						||
| Magnetplattenspeicher    | 8-12 ms      | 16*10^6    | 160 GB bis 4 TB            |
 | 
						||
| Platten-Farm oder -Array | 12 ms        | 24*10^6    | im TB- bis PB-Bereich      |
 | 
						||
 | 
						||
Typische Merkmale von Sekundärspeicher
 | 
						||
| Merkmal         | Kapazität | Latenz  | Bandbreite           |
 | 
						||
| --------------- | --------- | ------- | -------------------- |
 | 
						||
| 1983            | 30 MB     | 48.3 ms | 0.6 MB/s             |
 | 
						||
| 1994            | 4.3 GB    | 12.7 ms | 9 MB/s               |
 | 
						||
| 2003            | 73.4 GB   | 5.7 ms  | 86 MB/s              |
 | 
						||
| 2009            | 2 TB      | 5.1 ms  | 95 MB/s              |
 | 
						||
| 2019 SSD (NVMe) | 2 TB      | ??      | seq.read 3.500 MB/s  |
 | 
						||
|                 |           | ??      | seq.write 1.600 MB/s |
 | 
						||
 | 
						||
Solid State Disk (SSD)
 | 
						||
- basierend auf EEPROMs in NAND- oder NOR-Technologie
 | 
						||
- Arrays (=Flash-Block mit ca. 128 KB) von Speicherzellen, entweder ein Bit (SLC) oder 2-4 Bit (MLC)
 | 
						||
- MLC sind langsamer und haben verkürzte Lebensdauer
 | 
						||
- initial ist jedes Bit auf 1 gesetzt, durch Reprogrammieren auf 0
 | 
						||
- Löschen zurück auf 1 nur für ganzen Block
 | 
						||
- Konsequenz: langsames Löschen (Lesen = 25 μs, Löschen = 2 ms), begrenzte Lebensdauer (ca. 100.000    Lösch-Schreib-Zyklen)
 | 
						||
- Schnittstelle: SATA oder PCIe (NVMe)
 | 
						||
 | 
						||

 | 
						||
 | 
						||
SSDs in DBMS
 | 
						||
- klassische, auf sequenzielles Lesen ausgerichtete, Strategien von DBMS nutzen die Stärken von Flash-Speicher nicht aus
 | 
						||
- kleinere Blockgrößen lassen sich effizient adressieren, sollten aber ein Vielfaches der Flash-Seiten sein
 | 
						||
- wahlfreie Lesezugriffe sind effizienter als auf Magnetplatten, sollten aber auf Größen von ca. 4 bis 16 MB begrenzt werden
 | 
						||
- konkurrierende IO-Zugriffe sind bis zu einem gewissen Maße ohne negativen Performanzeinfluss durchführbar
 | 
						||
 | 
						||
**Speicherarrays: RAID**
 | 
						||
- Kopplung billiger Standardplatten unter einem speziellen Controller zu einem einzigen logischen Laufwerk
 | 
						||
- Verteilung der Daten auf die verschiedenen physischen Festplatten übernimmt Controller
 | 
						||
- zwei gegensätzliche Ziele:
 | 
						||
    - Erhöhung der Fehlertoleranz (Ausfallsicherheit, Zuverlässigkeit) durch Redundanz
 | 
						||
    - Effizienzsteigerung durch Parallelität des Zugriffs
 | 
						||
 | 
						||
Erhöhung der Fehlertoleranz
 | 
						||
- Nutzung zusätzlicher Platten zur Speicherung von Duplikaten (Spiegeln) der eigentlichen Daten => bei Fehler: Umschalten auf Spiegelplatte
 | 
						||
- bestimmte RAID-Levels (1, 0+1) erlauben eine solche Spiegelung
 | 
						||
- Alternative: Kontrollinformationen wie Paritätsbits nicht im selben Sektor wie die Originaldaten, sondern auf einer anderen Platte speichern
 | 
						||
- RAID-Levels 2 bis 6 stellen durch Paritätsbits oder Error Correcting Codes (ECC) fehlerhafte Daten wieder her
 | 
						||
- ein Paritätsbit kann einen Plattenfehler entdecken und bei Kenntnis der fehlerhaften Platte korrigieren
 | 
						||
 | 
						||
Erhöhung der Effizienz
 | 
						||
- Datenbank auf mehrere Platten verteilen, die parallel angesteuert werden können => Zugriffszeit auf große Datenmengen verringert sich fast linear mit der Anzahl der verfügbaren Platten
 | 
						||
- Verteilung: bit-, byte- oder blockweise
 | 
						||
- höhere RAID-Levels (ab Level 3) verbinden Fehlerkorrektur und block- oder bitweises Verteilen von Daten
 | 
						||
- Unterschiede:
 | 
						||
    - schnellerer Zugriff auf bestimmte Daten
 | 
						||
    - höherer Durchsatz für viele parallel anstehende Transaktionen durch eine Lastbalancierung des Gesamtsystems
 | 
						||
 | 
						||
 | 
						||
RAID-Levels
 | 
						||

 | 
						||

 | 
						||
 | 
						||
| Level | Striping blockweise | Striping bitweise | Kopie | Parität | Parität dedizierte Platte | Parität verteilt | Erkennen mehrerer Fehler |
 | 
						||
| ----- | ------------------- | ----------------- | ----- | ------- | ------------------------- | ---------------- | ------------------------ |
 | 
						||
| 0     | √                   |
 | 
						||
| 1     |                     |                   | √     |
 | 
						||
| 0+1   | √                   |                   | √     |
 | 
						||
| 2     |                     | √                 |       | √       |
 | 
						||
| 3     |                     | √                 |       | √       | √                         |
 | 
						||
| 4     | √                   |                   |       | √       | √                         |
 | 
						||
| 5     | √                   |                   |       | √       |                           | √                |
 | 
						||
| 6     | √                   |                   |       | √       |                           |                  | √                        |
 | 
						||
 | 
						||
**Sicherungsmedien: Tertiärspeicher**
 | 
						||
- weniger oft benutzte Teile der Datenbank, die eventuell sehr großen Umfang haben (Text, Multimedia) "billiger" speichern als auf Magnetplatten
 | 
						||
- aktuell benutzte Datenbestände zusätzlich sichern (archivieren)
 | 
						||
- Tertiärspeicher: Medium austauschbar
 | 
						||
    - offline: Medien manuell wechseln (optische Platten, Bänder)
 | 
						||
    - nearline: Medien automatisch wechseln (_Jukeboxes_, _Bandroboter_)
 | 
						||
 | 
						||
Langzeitarchivierung
 | 
						||
- Lebensdauer, Teilaspekte:
 | 
						||
- physische Haltbarkeit des Mediums garantiert die Unversehrtheit der Daten: 
 | 
						||
  - 10 Jahre für Magnetbänder, 
 | 
						||
  - 30 Jahre für optische Platten, 
 | 
						||
  - Papier???
 | 
						||
- Vorhandensein von Geräten und Treibern garantiert die Lesbarkeit von Daten: 
 | 
						||
  - Geräte für Lochkarten oder 8-Zoll-Disketten?
 | 
						||
- zur Verfügung stehende Metadaten garantieren die Interpretierbarkeit von Daten
 | 
						||
- Vorhandensein von Programmen, die auf den Daten arbeiten können, garantieren die Wiederverwendbarkeit von Daten
 | 
						||
 | 
						||
 | 
						||
## Struktur des Hintergrundspeichers
 | 
						||
Verwaltung des Hintergrundspeichers
 | 
						||
- Abstraktion von Speicherungs- oder Sicherungsmediums
 | 
						||
- Modell: Folge von Blöcken
 | 
						||
    
 | 
						||
- Alternativen:
 | 
						||
    - jede Relation oder jeder Zugriffspfad in genau einer Betriebssystem-Datei
 | 
						||
    - ein oder mehrere BS-Dateien, DBS verwaltet Relationen und Zugriffspfade selbst innerhalb dieser Dateien
 | 
						||
    - DBS steuert selbst Magnetplatte an und arbeitet mit den Blöcken in ihrer Ursprungsform ( _raw device_ )
 | 
						||
- Warum nicht immer BS-Dateiverwaltung?
 | 
						||
    - Betriebssystemunabhängigkeit
 | 
						||
    - In 32-Bit-Betriebssystemen: Dateigröße 4 GB maximal
 | 
						||
    - BS-Dateien auf maximal einem Medium
 | 
						||
    - betriebssystemseitige Pufferverwaltung von Blöcken des Sekundärspeichers im Hauptspeicher genügt nicht den Anforderungen des Datenbanksystems
 | 
						||
 | 
						||
Blöcke und Seiten
 | 
						||
- Zuordnung der physischen Blöcke zu Seiten
 | 
						||
- meist mit festen Faktoren: 1, 2, 4 oder 8 Blöcke einer Spur auf eine Seite
 | 
						||
- hier: "ein Block — eine Seite"
 | 
						||
- höhere Schichten des DBS adressieren über Seitennummer
 | 
						||
 | 
						||
Dienste des Dateisystems
 | 
						||
- Allokation oder Deallokation von Speicherplatz
 | 
						||
- Holen oder Speichern von Seiteninhalten
 | 
						||
- Allokation möglichst so, dass logisch aufeinanderfolgende Datenbereiche (etwa einer Relation) auch möglichst in aufeinanderfolgenden Blöcken der Platte gespeichert werden
 | 
						||
- Nach vielen Update-Operationen: Reorganisationsmethoden
 | 
						||
- Freispeicherverwaltung: doppelt verkettete Liste von Seiten
 | 
						||
 | 
						||
Abbildung der Datenstrukturen
 | 
						||
- Abbildung der konzeptuellen Ebene auf interne Datenstrukturen
 | 
						||
- Unterstützung durch Metadaten (im Data Dictionary, etwa das interne Schema)
 | 
						||
 | 
						||
| Konz. Ebene      | Interne Ebene   | Dateisystem/Platte |
 | 
						||
| ---------------- | --------------- | ------------------ |
 | 
						||
| Relationen ->    | Log. Dateien -> | Phys. Dateien      |
 | 
						||
| Tupel ->         | Datensätze ->   | Seiten/Blöcke      |
 | 
						||
| Attributwerte -> | Felder ->       | Bytes              |
 | 
						||
 | 
						||
- Beispiel: jede Relation in je einer logischen Datei, diese insgesamt in einer einzigen physischen Datei
 | 
						||
 | 
						||
 | 
						||
## Seiten, Sätze und Adressierung
 | 
						||
### Seite
 | 
						||
- Block:
 | 
						||
    - kleinste adressierbare Einheit auf Externspeicher
 | 
						||
    - Zuordnung zu Seiten im Hauptspeicher
 | 
						||
- Aufbau von Seiten
 | 
						||
    - Header
 | 
						||
       - Informationen über Vorgänger- und Nachfolger-Seite
 | 
						||
       - eventuell auch Nummer der Seite selbst
 | 
						||
       - Informationen über Typ der Sätze
 | 
						||
       - freier Platz
 | 
						||
    - Datensätze
 | 
						||
    - unbelegte Bytes
 | 
						||
 | 
						||
Seitenorganisation
 | 
						||
- Organisation der Seiten: doppelt verkettete Liste
 | 
						||
- freie Seiten in Freispeicherverwaltung
 | 
						||

 | 
						||
 | 
						||
Seite: Adressierung der Datensätze
 | 
						||
- adressierbare Einheiten
 | 
						||
    - Zylinder
 | 
						||
    - Spuren
 | 
						||
    - Sektoren
 | 
						||
    - Blöcke oder Seiten
 | 
						||
    - Datensätze in Blöcken oder Seiten
 | 
						||
    - Datenfelder in Datensätzen
 | 
						||
- Beispiel: Adresse eines Satzes durch Seitennummer und Offset (relative Adresse in Bytes vom Seitenanfang)
 | 
						||
 | 
						||
Seitenzugriff als Flaschenhals
 | 
						||
- Maß für die Geschwindigkeit von Datenbankoperationen: Anzahl der Seitenzugriffe auf dem Sekundärspeicher (wegen Zugriffslücke)
 | 
						||
- Faustregel: Geschwindigkeit des Zugriffs ⇐ Qualität des Zugriffspfades ⇐ Anzahl der benötigten Seitenzugriffe
 | 
						||
- Hauptspeicheroperationen nicht beliebig vernachlässigbar
 | 
						||
 | 
						||
 | 
						||
Einpassen von Datensätzen auf Blöcke
 | 
						||
- Datensätze (eventuell variabler Länge) in die aus einer fest vorgegebenen Anzahl von Bytes bestehenden Blöcke einpassen: Blocken
 | 
						||
- Blocken abhängig von variabler oder fester Feldlänge der Datenfelder
 | 
						||
       - Datensätze mit variabler Satzlänge: höherer Verwaltungsaufwand beim Lesen und Schreiben, Satzlänge immer wieder neu ermitteln
 | 
						||
       - Datensätze mit fester Satzlänge: höherer Speicheraufwand
 | 
						||
 | 
						||
Verschiedene Satztypen
 | 
						||

 | 
						||
 | 
						||
Sätze fester Länge
 | 
						||
- SQL: Datentypen fester und variabler Länge
 | 
						||
    - _char(n)_ Zeichenkette der festen Länge _n_
 | 
						||
    - _varchar(n)_ Zeichenkette variabler Länge mit der Maximallänge _n_
 | 
						||
- Aufbau der Datensätze, falls alle Datenfelder feste Länge:
 | 
						||
    1. Verwaltungsblock mit Typ eines Satzes (wenn unterschiedliche Satztypen auf einer Seite möglich) und Löschbit
 | 
						||
    2. Freiraum zur Justierung des Offset
 | 
						||
    3. Nutzdaten des Datensatzes
 | 
						||
 | 
						||
Sätze variabler Länge
 | 
						||
- im Verwaltungsblock nötig: Satzlänge _l_, um die Länge des Nutzdaten-Bereichs _d_ zu kennen
 | 
						||
    
 | 
						||
- Strategie a)
 | 
						||
    
 | 
						||
- Strategie b)
 | 
						||
    
 | 
						||
 | 
						||
Speicherung von Sätzen variabler Länge
 | 
						||
- Strategie a): Jedes Datenfeld variabler Länge $A_i$ beginnt mit einem _Längenzeiger $al_i$, der angibt, wie lang das folgende Datenfeld ist
 | 
						||
- Strategie b): Am Beginn des Satzes wird nach dem Satz-Längenzeiger _l_ und der Anzahl der Attribute ein Zeigerfeld $ap_1 ,..., ap_n$ für alle variabel langen Datenfelder eingerichtet
 | 
						||
- Vorteil Strategie b): leichtere Navigation innerhalb des Satzes (auch für Sätze in Seiten => TID)
 | 
						||
 | 
						||
Anwendung variabel langer Datenfelder
 | 
						||
- "Wiederholgruppen": Liste von Werten des gleichen Datentyps
 | 
						||
       - Zeichenketten variabler Länge wie _varchar(n)_ sind Wiederholgruppe mit _char_ als Basisdatentyp, mathematisch also die Kleene’sche Hülle $(char)∗$
 | 
						||
       - Mengen- oder listenwertige Attributwerte, die im Datensatz selbst denormalisiert gespeichert werden sollen (Speicherung als geschachtelte Relation oder Cluster-Speicherung), bei einer Liste von _integer_ -Werten wäre dies $(integer)∗$
 | 
						||
       - Adressfeld für eine Indexdatei, die zu einem Attributwert auf mehrere Datensätze zeigt (Sekundärindex), also $(pointer)∗$
 | 
						||
 | 
						||
Blockungstechniken: Nichtspannsätze
 | 
						||
- jeder Datensatz in maximal einem Block
 | 
						||
    
 | 
						||
- Standardfall (außer bei BLOBs oder CLOBs)
 | 
						||
 | 
						||
Blockungstechniken: Spannsätze
 | 
						||
- Spannsätze: Datensatz eventuell in mehreren Blöcken
 | 
						||
    
 | 
						||
 | 
						||
Adressierungstechniken
 | 
						||

 | 
						||
 | 
						||
Adressierung: TID-Konzept
 | 
						||
- Tupel-Identifier (TID) ist Datensatz-Adresse bestehend aus Seitennummer und Offset
 | 
						||
- Offset verweist innerhalb der Seite bei einem Offset-Wert von _i_ auf den _i_ -ten Eintrag in einer Liste von Tupelzeigern (Satzverzeichnis), die am Anfang der Seite stehen
 | 
						||
- Jeder Tupel-Zeiger enthält Offsetwert
 | 
						||
- Verschiebung auf der Seite: sämtliche Verweise von außen bleiben unverändert
 | 
						||
- Verschiebungen auf eine andere Seite: statt altem Datensatz neuer TID-Zeiger
 | 
						||
- diese zweistufige Referenz aus Effizienzgründen nicht wünschenswert: Reorganisation in regelmäßigen Abständen
 | 
						||
 | 
						||
 | 
						||
TID-Konzept: einstufige Referenz
 | 
						||

 | 
						||
 | 
						||
TID-Konzept: zweistufige Referenz
 | 
						||

 | 
						||
 | 
						||
## Alternative Speichermodelle
 | 
						||
- bisher klassisches N-äres Speichermodell (NSM), auch "row store"
 | 
						||
- Vorteile:
 | 
						||
    - gesamter Datensatz kann mit einem Seitenzugriff gelesen werden
 | 
						||
    - leichte Änderbarkeit einzelner Attributwerte
 | 
						||
- Nachteil:
 | 
						||
    - werden nur wenige Attributwerte benötigt, müssen trotzdem immer alle Attributwerte gelesen werden -> unnötiger IO-Aufwand
 | 
						||
- Alternativen: spaltenorientierte Speichermodelle
 | 
						||
    - Zerlegung einer _n_ -stelligen Relation in eine Menge von Projektionen (z.B. binäre Relation)
 | 
						||
    - Identifikation (und Rekonstruktion) über eine Schlüsselspalte oder Position
 | 
						||
 | 
						||
Spaltenorientierte Datenorganisation
 | 
						||

 | 
						||
 | 
						||
Alternative Speichermodelle: DSM
 | 
						||
- Decomposition Storage Model (DSM) -> column stores
 | 
						||
    - alle Werte einer Spalte (Attribut) werden hintereinander gespeichert
 | 
						||
    - Adressierung über Position
 | 
						||
        
 | 
						||
- Kompression einfach möglich (z.B. Run length encoding)
 | 
						||
- effizientere Scanoperationen (Feldoperationen -> bessere Cache-Nutzung)
 | 
						||
- jedoch: Updateoperationen sind komplexer, Lesen aller Spalten aufwendiger
 | 
						||
- Einsatz bei leseoptimierten Datenbanken
 | 
						||
 | 
						||
Ein Full-Table-Scan in NSM
 | 
						||
- Im NSM-Modell stehen alle Tupel einer Tabelle sequenziell hintereinander auf einer Datenbankseite.
 | 
						||
  
 | 
						||
 | 
						||
Ein "Full-Table-Scan" in DSM
 | 
						||
- Im DSM-Modell stehen alle Werte eines Attributs sequenziell hintereinander auf einer Datenbankseite.
 | 
						||
    
 | 
						||
- Alle Daten, die für den "l_shipdate Scan" geladen werden sind
 | 
						||
auch dafür relevant.
 | 
						||
 | 
						||
Alternative Speichermodelle: PAX
 | 
						||
- Partition Attributes Across (PAX) als Kompromiss
 | 
						||
    - NSM: alle Spalten eines Satzes auf der gleichen Seite
 | 
						||
    - DSM: vertikale Partitionierung, Miniseiten für jeweils eine Spalte
 | 
						||
    
 | 
						||
 | 
						||
## Main-Memory-Strukturen
 | 
						||
Speicherstrukturen für Main-Memory-Datenbanken
 | 
						||
- Vermeidung der seiten-basierten Indirektion (über Seitenadresse, Puffer)
 | 
						||
- Hauptspeicherzugriffe als neuer Bottleneck ("Memory Wall")
 | 
						||
- Cache-freundliche Datenstruktur: Hauptspeicherzugriffe tatsächlich nicht byteweise, sondern in Cachelines (64 Bytes)
 | 
						||
- Speicherlayout: Row Store vs. Column Store - abhängig vom Workload (Reduzierung der Cache Misses)
 | 
						||
- ggf. Partitionierung für Multicore-Systeme
 | 
						||
- Kompression der Daten zur Reduktion des Speicherbedarfs
 | 
						||
- Persistenz weiterhin notwendig, z.B. über Logging
 | 
						||
- Bsp.: In-Memory-Datenstruktur für relationale Column Stores
 | 
						||
       - pro Spalte = Feld von Attributwerten
 | 
						||
       - Kompression der Attributwerte (siehe Kapitel 8)
 | 
						||
       - ggf. Strukturierung in Segmemten (Chunks) für bessere Speicherverwaltung, NUMA-Effekte
 | 
						||
 | 
						||
 | 
						||
## Speicherorganisation in konkreten DBMS
 | 
						||
Oracle: Datenbankstruktur
 | 
						||
    
 | 
						||
 | 
						||
Oracle: Blöcke
 | 
						||
    
 | 
						||
 | 
						||
Oracle: Aufbau von Datensätzen
 | 
						||
    
 | 
						||
- Kettadresse für _Row Chaining_ : Verteilung und Verkettung zu großer Datensätze (> 255 Spalten) über mehrere Blöcke
 | 
						||
- row id = (data object identifier, data file identifier, block identifier, row identifier)
 | 
						||
 | 
						||
Zusammenfassung
 | 
						||
- Speicherhierarchie und Zugriffslücke
 | 
						||
- Speicher- und Sicherungsmedien
 | 
						||
- Hintergrundspeicher: Blockmodell
 | 
						||
- Einpassen von Sätzen in Seiten
 | 
						||
- Satzadressierung: TID-Konzept
 | 
						||
 | 
						||
# Caching und Pufferverwaltung
 | 
						||
## Aufgaben
 | 
						||
Aufgaben der Pufferverwaltung
 | 
						||
- Puffer: ausgezeichneter Bereich des Hauptspeichers
 | 
						||
- in Pufferrahmen gegliedert, jeder Pufferrahmen kann Seite der Platte aufnehmen
 | 
						||
- Aufgaben:
 | 
						||
    - Pufferverwaltung muss angeforderte Seiten im Puffer suchen => effizienteSuchverfahren
 | 
						||
    - parallele Datenbanktransaktionen: geschickte Speicherzuteilung im Puffer
 | 
						||
    - Puffer gefüllt: adäquate Seitenersetzungsstrategien
 | 
						||
    - Unterschiede zwischen einem Betriebssystem-Puffer und _einem Datenbank-Puffer_
 | 
						||
    - spezielle Anwendung der Pufferverwaltung: Schattenspeicherkonzept
 | 
						||

 | 
						||
 | 
						||
Mangelnde Eignung des BS-Puffers
 | 
						||
- Natürlicher Verbund von Relationen $a$ und $b$ (zugehörige Folge von Seiten: _Ai_ bzw. _Bj_ )
 | 
						||
- Implementierung: _Nested-Loop_
 | 
						||
    
 | 
						||
- Ablauf
 | 
						||
    - FIFO: $A_1$ verdrängt, da älteste Seite im Puffer
 | 
						||
    - LRU: $A_1$ verdrängt, da diese Seite nur im ersten Schritt beim Auslesen des ersten Vergleichstupels benötigt wurde
 | 
						||
- Problem
 | 
						||
    - im nächsten Schritt wird das zweite Tupel von $A_1$ benötigt
 | 
						||
    - weiteres "Aufschaukeln": um $A_1$ laden zu können, muss $B_1$ entfernt werden (im nächsten Schritt benötigt) usw.
 | 
						||
 | 
						||
## Suche von Seiten und Speicherzuteilung
 | 
						||
Suchen einer Seite
 | 
						||
- Direkte Suche:
 | 
						||
    - ohne Hilfsmittel linear im Puffer suchen
 | 
						||
- Indirekte Suche:
 | 
						||
    - Suche nur noch auf einer kleineren Hilfsstruktur
 | 
						||
    - _unsortierte und sortierte Tabelle_ : alle Seiten im Puffer vermerkt
 | 
						||
    - _verkettete Liste_ : schnelleres sortiertes Einfügen möglich
 | 
						||
    - _Hashtabelle_ : bei geschickt gewählter Hashfunktion günstigster Such- und Änderungsaufwand
 | 
						||
 | 
						||
 | 
						||
Speicherzuteilung im Puffer
 | 
						||
- bei mehreren parallel anstehenden Transaktionen
 | 
						||
    - Lokale Strategien: Jeder Transaktion bestimmte disjunkte Pufferteile verfügbar machen (Größe statisch vor Ablauf der Transaktionen oder dynamisch zur Programmlaufzeit entscheiden)
 | 
						||
    - Globale Strategien: Zugriffsverhalten aller Transaktionen insgesamt bestimmt Speicherzuteilung (gemeinsam von mehreren Transaktionen referenzierte Seiten können so besser berücksichtigt werden)
 | 
						||
    - Seitentypbezogene Strategien: Partition des Puffers: Pufferrahmen für Datenseiten, Zugriffspfadseiten, Data-Dictionary-Seiten, usw. - eigene Ersetzungstrategien für die jeweiligen Teile möglich
 | 
						||
 | 
						||
## Seitenersetzungsstrategien
 | 
						||
- Speichersystem fordert Seite $E_2$ an, die nicht im Puffer vorhanden ist
 | 
						||
- Sämtliche Pufferrahmen sind belegt
 | 
						||
- vor dem Laden von _E_ 2 Pufferrahmen freimachen
 | 
						||
- nach den unten beschriebenen Strategien Seite aussuchen
 | 
						||
- Ist Seite in der Zwischenzeit im Puffer verändert worden, so wird sie nun auf Platte zurückgeschrieben
 | 
						||
- Ist Seite seit Einlagerung in den Puffer nur gelesen worden, so kann sie überschrieben werden (verdrängt)
 | 
						||
 | 
						||

 | 
						||
 | 
						||
Seitenersetzung in DBMS
 | 
						||
- Fixieren von Seiten (Pin oder Fix):
 | 
						||
    - Fixieren von Seiten im Puffer verhindert das Verdrängen
 | 
						||
    - speziell für Seiten, die in Kürze wieder benötigt werden
 | 
						||
- Freigeben von Seiten (Unpin oder Unfix):
 | 
						||
    - Freigeben zum Verdrängen
 | 
						||
    - speziell für Seiten, die nicht mehr benötigt werden
 | 
						||
- Zurückschreiben einer Seite:
 | 
						||
    - Auslösen des Zurückschreibens für geänderte Seiten bei Transaktionsende
 | 
						||
 | 
						||
Seitenersetzung: Verfahren
 | 
						||
- grundsätzliches Vorgehen beim Laden einer Seite:
 | 
						||
    - Demand-paging-Verfahren: genau eine Seite im Puffer durch angeforderte Seite ersetzen
 | 
						||
    - Prepaging-Verfahren: neben der angeforderten Seite auch weitere Seiten in den Puffer einlesen, die eventuell in der Zukunft benötigt werden (z.B. bei BLOBs sinnvoll)
 | 
						||
- Ersetzen einer Seite im Puffer:
 | 
						||
    - optimale Strategie: Welche Seite hat maximale Distanz zu ihrem nächsten Gebrauch? (nicht realisierbar, zukünftiges Referenzverhalten nicht vorhersehbar) -> Realisierbare Verfahren besitzen keine Kenntnisse über das zukünftige Referenzverhalten
 | 
						||
    - Zufallsstrategie: jeder Seite gleiche Wiederbenutzungswahrscheinlichkeit zuordnen
 | 
						||
 | 
						||

 | 
						||
 | 
						||
Fehlseitenrate
 | 
						||
$$F=1-p(\frac{1-F_{kalt}}{p_{DB}}) * 100%$$
 | 
						||
- $p$: Puffergröße
 | 
						||
- $p_{DB}$: Puffergröße, die gesamte Datenbank umfasst
 | 
						||
- $F_{kalt}$: Fehlseitenrate beim Kaltstart (d.h. leerer Puffer) -> Verhältnis von Anzahl der in den Puffer geladenen Seiten zur Anzahl der Referenzierungen
 | 
						||
 | 
						||
- Gute, realisierbare Verfahren sollen vergangenes Referenzverhalten auf Seiten nutzen, um Erwartungswerte für Wiederbenutzung schätzen zu können
 | 
						||
       - besser als Zufallsstrategie
 | 
						||
       - Annäherung an optimale Strategie
 | 
						||
 | 
						||
 | 
						||
Merkmale gängiger Strategien
 | 
						||
- Alter der Seite im Puffer:
 | 
						||
    - Alter einer Seite nach Einlagerung (die globale Strategie (G))
 | 
						||
    - Alter einer Seite nach dem letztem Referenzzeitpunkt (die Strategie des jüngsten Verhaltens (J))
 | 
						||
    - Alter einer Seite wird nicht berücksichtigt (-)
 | 
						||
- Anzahl der Referenzen auf Seite im Puffer:
 | 
						||
    - Anzahl aller Referenzen auf eine Seite (die globale Strategie (G))
 | 
						||
    - Anzahl nur der letzten Referenzen auf eine Seite (die Strategie des jüngsten Verhaltens (J))
 | 
						||
    - Anzahl der Referenzen wird nicht berücksichtigt (-)
 | 
						||
 | 
						||
 | 
						||
Gängige Strategien
 | 
						||

 | 
						||
 | 
						||
Klassifikation gängiger Strategien
 | 
						||
| Verfahren                        | Prinzip                                                             | Alter | Anzahl |
 | 
						||
| -------------------------------- | ------------------------------------------------------------------- | ----- |
 | 
						||
| FIFO                             | älteste Seite ersetzt                                               | G     | -      |
 | 
						||
| LFU (least fre-quently used)     | Seite mit geringster Häufigkeit ersetzen                            | -     | G      |
 | 
						||
| LRU (least recently used)        | Seite ersetzen, die am längsten nicht referenziert wurde (System R) | J     | J      |
 | 
						||
| DGCLOCK (dyn. generalized clock) | Protokollierung der Ersetzungshäufigkeiten wichtiger Seiten         | G     | JG     |
 | 
						||
| LRD (least reference density)    | Ersetzung der Seite mit geringster Referenzdichte                   | JG    | G      |
 | 
						||
 | 
						||
Beispiel
 | 
						||
- Folge von Seitenanforderungen #1, #2 ...
 | 
						||
- Puffer der Größe 6
 | 
						||
    
 | 
						||
- Ablauf mit
 | 
						||
    - FIFO ...
 | 
						||
    - LFU ...
 | 
						||
 | 
						||
### LRU: Least Recently Used
 | 
						||
- Idee: Seite im Puffer ersetzen, die am längsten nicht mehr referenziert wurde
 | 
						||
- Implementierung:
 | 
						||
    - Liste oder Stack von Seiten
 | 
						||
    - Puffer-Hit bewegt Seite zur MRU-Position (Most Recently Used)
 | 
						||
    - Seite am Ende wird verdrängt
 | 
						||
    
 | 
						||
- Varianten:
 | 
						||
    - durch Interpretation der Pin-Operation: Least Recently Referenced bzw. Least Recently Unfixed
 | 
						||
    - durch Berücksichtigung der letzten $k$ Referenzierungen (d.h. auch Häufigkeit): LRU-K
 | 
						||
 | 
						||
LRU: Probleme
 | 
						||
- Lock Contention in Multitasking-Umgebungen
 | 
						||
    - Zugriff auf LRU-Liste/Stack und Bewegung der Seite erfordert exklusiven Zugriff auf Datenstruktur
 | 
						||
    - aufwendige Operation
 | 
						||
- berücksichtigt nur Alter jedoch nicht Häufigkeit
 | 
						||
    - oft gelesene Seiten mit langen Pausen zwischen den Zugriffen werden nicht adäquat berücksichtigt
 | 
						||
- "Zerstörung" des Puffers durch Scan-Operator
 | 
						||
    - Seiten werden nur einmalig gelesen, verdrängen jedoch andere (ältere) Seiten
 | 
						||
 | 
						||
 | 
						||
Lock Contention bei der Pufferverwaltung
 | 
						||

 | 
						||
- Sperren = Latches: leichtgewichtige (wenige CPU-Instruktionen) Objekte für kurzzeitige Sperren
 | 
						||
 | 
						||
Approximierende Verfahren
 | 
						||
- Idee:
 | 
						||
    - Vereinfachung der benötigten Datenstruktur durch Approximation
 | 
						||
    - Effektivität (Trefferrate) vs. Skalierbarkeit (Anzahl der Threads)
 | 
						||
- CLOCK: Approximation der Historie durch Bit-Schieberegister der Länge $k$
 | 
						||
       - $k= 0$: FIFO
 | 
						||
       - $k\rightarrow\infty$: LRU
 | 
						||
       - typisch: $k = 1$
 | 
						||
 | 
						||
### CLOCK
 | 
						||
- Seite mit Benutzt-Bit; bei Referenzierung auf "1" setzen
 | 
						||
- bei Seitenfehler:
 | 
						||
    - zyklische Suche
 | 
						||
    - Seite mit "0" verdrängen
 | 
						||
    - sonst Setzen auf "0"
 | 
						||
    
 | 
						||
 | 
						||
### GCLOCK
 | 
						||
- Verbesserung: Benutzt-Bit durch Referenzzähler _RC_ ersetzen; Dekrementierung bei Suche
 | 
						||
- weitere Verbesserungen:
 | 
						||
    - Initialisierung des Referenzzählers
 | 
						||
    - Inkrementierung des Zählers
 | 
						||
    - seitentypspezifische Maßnahmen (für Typ _i_ : Seitengewicht $E_i$ bei Erstreferenzierung, $W_i$ bei weiterer Referenzierung)
 | 
						||
    - Altern
 | 
						||
- Varianten: Seite _j_ von Typ _i_
 | 
						||
    - $GCLOCK(V1): RC_j := E_i ; RC_j := RC_j + W_i$
 | 
						||
    - $GCLOCK(V2): RC_j := E_i ; RC_j := W_i$ (speziell für $W_i\geq E_i$)
 | 
						||
 | 
						||
### DGCLOCK
 | 
						||
- weitere Verbesserung: globaler Zähler $GC$ und Normierung der aktuellen Referenzzähler $RC$
 | 
						||
    1. Initialisierung: $RC_j := GC$
 | 
						||
    2. Referenzierung von Seite $j : GC := GC + 1 ; RC_j := RC_j + GC$
 | 
						||
    3. bei Überschreiten $GC > MIN : \forall j : RC_j := RC_j / C$
 | 
						||
 | 
						||
### ARC
 | 
						||
- Adaptive Replacement Cache: neues Verfahren, das Nachteile von LRU vermeidet
 | 
						||
- Prinzip:
 | 
						||
    - Puffergröße _c_
 | 
						||
    - Pufferverzeichnis für 2 _c_ Seiten: _c_ Pufferseiten + _c_ History-Seiten
 | 
						||
    - Liste _L_ 1 : "recency" = kurzfristiger Nutzen-> Seiten, die kürzlich einmal gelesen wurden
 | 
						||
    - Liste _L_ 2 : "frequency" = langfristiger Nutzen -> Seiten, die kürzlich mehrmals gelesen wurden
 | 
						||
- Ausgangspunkt: einfache Verdrängungsstrategie DBL(2 _c_ )
 | 
						||
    - Ersetze die LRU-Seite in $L_1$, wenn $|L_1| = c$ , sonst ersetze LRU-Seite in $L_2$
 | 
						||
    - Ziel: Größenausgleich zwischen $L_1$ und $L_2$
 | 
						||
    - Zugriff Seite $p$: wenn Treffer -> $p$ wird MRU in $L_2$ , sonst in $L_1$
 | 
						||
 | 
						||
Von DBL(2c) zu ARC
 | 
						||

 | 
						||
- Parameter $p$ mit $0\leq p \leq c$
 | 
						||
    - $T_1$ enthält $p$ Seiten, $T_2$ enthält $c-p$ Seiten
 | 
						||
- Wahl von $p$?
 | 
						||
 | 
						||
ARC: Algorithmus
 | 
						||
- Seitenanforderungen: $x_1,x_2 ,..., x_t ,...$
 | 
						||
- $p = 0, T_1 , B_1 , T_2 ,B_2$ sind initial leer
 | 
						||
    - Fall 1: $x_t \in T_1 \cup T_2$ /* Puffer-Hit */
 | 
						||
        - Bewege $x_t$ zu MRU von $T_2$
 | 
						||
    - Fall 2: $x_t \in B_1$
 | 
						||
        - Anpassung: $p = min\{ p +\delta_1,c\}$ mit $\delta_1 = \begin{cases} 1\quad\text{ wenn } |B_1|\geq |B_2| \\ \frac{|B_2|}{|B_1|} \quad\text{ sonst}\end{cases}$
 | 
						||
        - $REPLACE(x_t,p)$
 | 
						||
        - Bewege $x_t$ von $B_1$ zu MRU von $T_2$
 | 
						||
    - Fall 3: $x_t \in B_2$
 | 
						||
      - Anpassung: $p = max\{ p - \delta_2, 0 \}$ mit $\delta_2 = \begin{cases} 1\quad\text{ wenn } |B_2|\geq |B_1| \\ \frac{|B_1|}{|B_2|} \quad\text{ sonst}\end{cases}$
 | 
						||
      - $REPLACE(x_t,p)$
 | 
						||
      - Bewege $x_t$ von $B_2$ zu MRU von $T_2$
 | 
						||
    - Fall 4: $x_t \not\in T_1 \cup B_1 \cup T_2 \cup B_2$
 | 
						||
      - 4.A: $|L_1| = c$
 | 
						||
        - Wenn $|T_1|<c$, lösche LRU in $B_1$, $REPLACE(x_t,p)$
 | 
						||
        - sonst /* $B_1 = \varnothing$ */ lösche LRU in $T_1$
 | 
						||
      - 4.B: $|L_1|<c$
 | 
						||
        - Wenn $|L_1|+|L_2|\geq c$ , lösche LRU in $B_2$, $REPLACE(x_t,p)$
 | 
						||
      - Bewege $x_t$ zu MRU in $T_1$
 | 
						||
 | 
						||
ARC: $REPLACE(x_t, p)$
 | 
						||
 | 
						||
    if $|T_1|>p$ oder ($x_t\in B_2$ und $|T_1|=p$)
 | 
						||
        Lösche LRU-Seite in $T_1$ und bewege sie zu MRU in $B_1$
 | 
						||
    else
 | 
						||
        Lösche LRU-Seite in $T_2$ und bewege sie zu MRU in $B_2$
 | 
						||
    endif
 | 
						||
 | 
						||
ARC: Beispiel
 | 
						||
1. erstmalige Anforderung der Seiten $#1$ und $#2$: Aufnahme in
 | 
						||
    
 | 
						||
2. nächsten Referenzierung von $#1$: Übernahme in $T_2$-Liste
 | 
						||
    
 | 
						||
3. Seitenanforderungen $#3$, $#4$, $#1$; mit $#2$ wird diese in $T_2$ bewegt; Platz für Seite $#5$:
 | 
						||
    
 | 
						||
4. Beantwortung der Seitenanforderungen $#1$ und $#2$ aus $T_2$
 | 
						||
5. neu angeforderten Seiten $#5$ und $#6$ in $T_1$
 | 
						||
    
 | 
						||
6. Seitenanforderung $#7$: Verdrängen von $#4$ aus $T_1$ in $B_1$
 | 
						||
    
 | 
						||
 | 
						||
ARC: Eigenschaften
 | 
						||
- kontinuierliche Anpassung von Parameter $p$
 | 
						||
    - Lernraten $\delta_1$ und $\delta_2$
 | 
						||
    - "Investieren in Liste mit dem meisten Profit"
 | 
						||
- Berücksichtigung von Alter und Häufigkeit
 | 
						||
    - durch zwei Listen $L_1$ und $L_2$
 | 
						||
- Scan-Resistenz
 | 
						||
    - einmalig gelesene Seiten nur in $L_1$, niemals in $L_2$
 | 
						||
- Vermeidung von Lock Contention durch approximierende Varianten (CAR, CART, ...)
 | 
						||
 | 
						||
## Fazit
 | 
						||
- Pufferverwaltungsstrategie mit großem Einfluss auf Performance
 | 
						||
- in kommerziellen Systemen meist LRU mit Variationen
 | 
						||
- besondere Behandlung von Full-Table-Scans
 | 
						||
- weiterer Einflussfaktor: Puffergröße
 | 
						||
- Indikator: Trefferrate (engl. _hit ratio_ )
 | 
						||
    $$hit\_ratio = \frac{\text{Anz. log. Zugriffe} - \text{Anz. phys. Zugriffe}}{\text{Anz. log. Zugriffe}}$$
 | 
						||
- 5-Minuten-Regel (Gray, Putzolu 1997):
 | 
						||
    Daten, die in den nächsten 5 Min. wieder referenziert werden, sollten im Hauptspeicher gehalten werden
 | 
						||
 | 
						||
# Indexierung von Daten
 | 
						||
## Klassifikation der Speichertechniken
 | 
						||
Einordnung in 5-Schichten-Architektur
 | 
						||
- **Speichersystem** fordert über Systempufferschnittstelle Seiten an
 | 
						||
- interpretiert diese als interne Datensätze
 | 
						||
- interne Realisierung der logischen Datensätze mit Hilfe von Zeigern, speziellen Indexeinträgen und weiteren Hilfsstrukturen
 | 
						||
- Zugriffssystem abstrahiert von der konkreten Realisierung
 | 
						||
 | 
						||
Klassifikation der Speichertechniken
 | 
						||
- Kriterien für Zugriffsstrukturen oder Zugriffsverfahren:
 | 
						||
    - organisiert interne Relation selbst (Dateiorganisationsform) oder zusätzliche Zugriffsmöglichkeit auf bestehende interne Relation (Zugriffspfad)
 | 
						||
    - Art der Zuordnung von gegebenen Attributwerten zu Datensatz-Adressen: Schlüsselvergleich = Zuordnung von Schlüsselwert zu Adresse über Hilfsstruktur; Schlüsseltransformation = Berechnung der Adresse aus Schlüsselwert (z.B. über Hashfunktion)
 | 
						||
    - Arten von Anfragen, die durch Dateiorganisationsformen und Zugriffspfade effizient unterstützt werden können
 | 
						||

 | 
						||
 | 
						||
Dünn- vs. dichtbesetzter Index
 | 
						||
- dünnbesetzter Index: nicht für jeden Zugriffsattributwert $k$ ein Eintrag in Indexdatei sondern z.B. nur für _Seitenanführer_ einer sortierten Relation
 | 
						||
- dichtbesetzter Index: für jeden Datensatz der internen Relation ein Eintrag in Indexdatei
 | 
						||
 | 
						||
Geclusterter vs. nicht-geclusterter Index
 | 
						||
- geclusterter Index: in der gleichen Form sortiert wie interne Relation
 | 
						||
- nicht-geclusterter Index: anders organisiert als interne Relation
 | 
						||
- Primärindex oft dünnbesetzt und geclustert
 | 
						||
- jeder dünnbesetzte Index ist auch geclusterter Index, aber nicht umgekehrt
 | 
						||
- Sekundärindex kann nur dichtbesetzter, nicht-geclusterter Index sein (auch: invertierte Datei)
 | 
						||

 | 
						||
 | 
						||
Statische vs. dynamische Struktur
 | 
						||
- statische Zugriffsstruktur: optimal nur bei bestimmter (fester) Anzahl von verwaltenden Datensätzen
 | 
						||
- dynamische Zugriffsstruktur: unabhängig von der Anzahl der Datensätze optimal
 | 
						||
       - dynamische Adresstransformationsverfahren verändern dynamisch Bildbereich der Transformation
 | 
						||
       - dynamische Indexverfahren verändern dynamisch Anzahl der Indexstufen => in DBS üblich
 | 
						||
 | 
						||
Klassifikation
 | 
						||

 | 
						||
 | 
						||
 | 
						||
## Statische Verfahren
 | 
						||
- Heap, indexsequenziell, indiziert-nichtsequenziell
 | 
						||
- oft grundlegende Speichertechnik in RDBS
 | 
						||
- direkte Organisationsformen: keine Hilfsstruktur, keine Adressberechnung (Heap, sequenziell)
 | 
						||
- statische Indexverfahren für Primärindex und Sekundärindex
 | 
						||
 | 
						||
 | 
						||
Statische Verfahren: Überblick
 | 
						||

 | 
						||
 | 
						||
### Heap
 | 
						||
Organisation
 | 
						||
- völlig unsortiert speichern
 | 
						||
- physische Reihenfolge der Datensätze ist zeitliche Reihenfolge der Aufnahme von Datensätzen
 | 
						||
    |      |       |            |     |
 | 
						||
    | ---- | ----- | ---------- | --- | -------- |
 | 
						||
    | 8832 | Max   | Mustermann | ... | 9.1.2003 |
 | 
						||
    | 5588 | Beta  | Alpha      | ... | 7.3.1978 |
 | 
						||
    | 4711 | Gamma | Delta      | ... | 2.5.1945 |
 | 
						||
 | 
						||
Operationen
 | 
						||
- insert: Zugriff auf letzte Seite der Datei. Genügend freier Platz => Satz anhängen. Sonst nächste freie Seite holen
 | 
						||
- delete: lookup, dann Löschbit auf 0 gesetzt
 | 
						||
- lookup: sequenzielles Durchsuchen der Gesamtdatei, maximaler Aufwand (Heap-Datei meist zusammen mit Sekundärindex eingesetzt; oder für sehr kleine Relationen)
 | 
						||
- Komplexitäten:
 | 
						||
    - Neuaufnahme von Daten $O(1)$
 | 
						||
    - Suchen $O(n)$
 | 
						||
 | 
						||
### Sequenzielle Speicherung
 | 
						||
- sortiertes Speichern der Datensätze
 | 
						||
    |      |       |            |     |
 | 
						||
    | ---- | ----- | ---------- | --- | -------- |
 | 
						||
    | 4711 | Gamma | Delta      | ... | 2.5.1945 |
 | 
						||
    | 5588 | Beta  | Alpha      | ... | 7.3.1978 |
 | 
						||
    | 8832 | Max   | Mustermann | ... | 9.1.2003 |
 | 
						||
 | 
						||
Sequenzielle Datei: Operationen
 | 
						||
- insert: Seite suchen, Datensatz einsortieren => beim Anlegen oder sequenziellen Füllen einer Datei jede Seite nur bis zu gewissem Grad (etwa 66%) füllen
 | 
						||
- delete: Aufwand bleibt
 | 
						||
- Folgende Dateiorganisationsformen:
 | 
						||
    - schnelleres lookup
 | 
						||
    - mehr Platzbedarf (durch Hilfsstrukturen wie Indexdateien)
 | 
						||
    - mehr Zeitbedarf bei insert und delete
 | 
						||
- klassische Indexform: indexsequenzielle Dateiorganisation
 | 
						||
 | 
						||
## Indexsequenzielle Dateiorganisation
 | 
						||
- Kombination von sequenzieller Hauptdatei und Indexdatei: indexsequenzielle Dateiorganisationsform
 | 
						||
- Indexdatei kann geclusterter, dünnbesetzter Index sein
 | 
						||
- mindestens zweistufiger Baum
 | 
						||
    - Blattebene ist Hauptdatei (Datensätze)
 | 
						||
    - jede andere Stufe ist Indexdatei
 | 
						||

 | 
						||
 | 
						||
Aufbau der Indexdatei
 | 
						||
- Datensätze in Indexdatei: _(Primärschlüsselwert, Seitennummer)_ zu jeder Seite der Hauptdatei genau ein Index-Datensatz in Indexdatei
 | 
						||
- Problem: "Wurzel" des Baumes bei einem einstufigen Index nicht nur eine Seite
 | 
						||

 | 
						||
 | 
						||
 | 
						||
Mehrstufiger Index
 | 
						||
- Optional: Indexdatei wieder indexsequenziell verwalten
 | 
						||
- Idealerweise: Index höchster Stufe nur noch eine Seite
 | 
						||

 | 
						||
 | 
						||
lookup bei indexsequenziellen Dateien
 | 
						||
- lookup-Operation sucht Datensatz zum Zugriffsattributwert _w_
 | 
						||
- Indexdatei sequenziell durchlaufen, dabei $(v_1,s)$ im Index gesucht mit $v_1\leq w$:
 | 
						||
    - $(v_1,s)$ ist letzter Satz der Indexdatei, dann kann Datensatz zu _w_ höchstens auf dieser Seite gespeichert sein (wenn er existiert)
 | 
						||
    - nächster Satz $(v_2,s′)$ im Index hat $v_2 > w$ , also muss Datensatz zu _w_, wenn vorhanden, auf Seite _s_ gespeichert sein
 | 
						||
- $(v_1,s)$ überdeckt Zugriffsattributwert _w_
 | 
						||
 | 
						||
insert bei indexsequenziellen Dateien
 | 
						||
- insert: zunächst mit lookup Seite finden
 | 
						||
- Falls Platz, Satz sortiert in gefundener Seite speichern; Index anpassen, falls neuer Satz der erste Satz in der Seite
 | 
						||
- Falls kein Platz, neue Seite von Freispeicherverwaltung holen; Sätze der "zu vollen" Seite gleichmäßig auf alte und neue Seite verteilen; für neue Seite Indexeintrag anlegen
 | 
						||
- Alternativ neuen Datensatz auf Überlaufseite zur gefundenen Seite
 | 
						||
 | 
						||
delete bei indexsequenziellen Dateien
 | 
						||
- delete: zunächst mit lookup Seite finden
 | 
						||
- Satz auf Seite löschen (Löschbit auf 0)
 | 
						||
- erster Satz auf Seite: Index anpassen
 | 
						||
- Falls Seite nach Löschen leer: Index anpassen, Seite an Freispeicherverwaltung zurück
 | 
						||
 | 
						||
Probleme indexsequenzieller Dateien
 | 
						||
- stark wachsende Dateien: Zahl der linear verketteten Indexseiten wächst; automatische Anpassung der Stufenanzahl nicht vorgesehen
 | 
						||
- stark schrumpfende Dateien: nur zögernde Verringerung der Index- und Hauptdatei-Seiten
 | 
						||
- unausgeglichene Seiten in der Hauptdatei (unnötig hoher Speicherplatzbedarf, zu lange Zugriffszeit)
 | 
						||
 | 
						||
 | 
						||
Indiziert-nichtsequenzieller Zugriffspfad
 | 
						||
- zur Unterstützung von Sekundärschlüsseln
 | 
						||
- mehrere Zugriffpfade dieser Form pro Datei möglich
 | 
						||
- einstufig oder mehrstufig: höhere Indexstufen wieder indexsequenziell organisiert
 | 
						||
 | 
						||
Aufbau der Indexdatei
 | 
						||
- Sekundärindex, dichtbesetzter und nicht-geclusteter Index
 | 
						||
- zu jedem Satz der Hauptdatei Satz $(w,s)$ in der Indexdatei
 | 
						||
- _w_ Sekundärschlüsselwert, _s_ zugeordnete Seite
 | 
						||
    - entweder für ein _w_ mehrere Sätze in die Indexdatei aufnehmen
 | 
						||
    - oder für ein _w_ Liste von Adresse in der Hauptdatei angeben
 | 
						||

 | 
						||
 | 
						||
Operationen
 | 
						||
- lookup: _w_ kann mehrfach auftreten, Überdeckungstechnik nicht benötigt
 | 
						||
- insert: Anpassen der Indexdateien
 | 
						||
- delete: Indexeintrag entfernen
 | 
						||
 | 
						||
Probleme statischer Verfahren
 | 
						||
- unzureichende Anpassung an wachsende/schrumpfende Datenmengen
 | 
						||
- schlechte Ausnutzung von Speicher nach Seitensplits
 | 
						||
- Bevorzugung bestimmter Attribute (Schlüssel)
 | 
						||
- daher in den folgenden Kapiteln:
 | 
						||
    - bessere Datenstrukturen zur Schlüsselsuche als zusätzlicher Zugriffspfad = Approximation einer Funktion Schlüssel -> Speicheradresse, z.B. über Baumverfahren
 | 
						||
    - Erweiterung von Hashverfahren um Anpassung des Bildbereichs = dynamische Hashverfahren
 | 
						||
    - Behandlung von zusammengesetzten Schlüsseln = multidimensionale Zugriffsverfahren, z.B. multidimensionale Bäume oder raumfüllende Kurven
 | 
						||
 | 
						||
# Baumbasierte Indexstrukturen
 | 
						||
## Baumverfahren
 | 
						||
- Stufenanzahl dynamisch verändern
 | 
						||
- wichtigste Baumverfahren: B-Bäume und ihre Varianten
 | 
						||
- B-Baum-Varianten sind noch "allgegenwärtiger" in heutigen Datenbanksystemen als SQL
 | 
						||
- SQL nur in der relationalen und objektrelationalen Datenbanktechnologie verbreitet; B-Bäume überall als Grundtechnik eingesetzt
 | 
						||
 | 
						||
Baumverfahren: Überblick
 | 
						||

 | 
						||
 | 
						||
## B-Bäume
 | 
						||
- Ausgangspunkt: ausgeglichener, balancierter Suchbaum
 | 
						||
- Ausgeglichen oder balanciert: alle Pfade von der Wurzel zu den Blättern des Baumes gleich lang
 | 
						||
- Hauptspeicher-Implementierungsstruktur: binäre Suchbäume, beispielsweise AVL-Bäume von Adelson-Velskii und Landis
 | 
						||
- Datenbankbereich: Knoten der Suchbäume zugeschnitten auf Seitenstruktur des Datenbanksystems
 | 
						||
- mehrere Zugriffsattributwerte auf einer Seite
 | 
						||
- Mehrwegebäume
 | 
						||
 | 
						||
Prinzip des B-Baumes
 | 
						||
- B-Baum von Bayer (B für balanciert, breit, buschig, Bayer, NICHT: binär)
 | 
						||
- dynamischer, balancierter Indexbaum, bei dem jeder Indexeintrag auf eine Seite der Hauptdatei zeigt
 | 
						||
- Mehrwegebaum völlig ausgeglichen, wenn
 | 
						||
  1. alle Wege von Wurzel bis zu Blättern gleich lang
 | 
						||
  2. jeder Knoten gleich viele Indexeinträge
 | 
						||
- vollständiges Ausgleichen zu teuer, deshalb B-Baum-Kriterium:
 | 
						||
    Jede Seite außer der Wurzelseite enthält zwischen $m$ und $2m$ Einträge.
 | 
						||
 | 
						||
Definition B-Baum
 | 
						||
- Ordnung eines B-Baumes ist minimale Anzahl der Einträge auf den Indexseiten außer der Wurzelseite
 | 
						||
- Bsp.: B-Baum der Ordnung 8 fasst auf jeder inneren Indexseite zwischen 8 und 16 Einträgen
 | 
						||
- Def.: Ein Indexbaum ist ein B-Baum der Ordnung $m$ , wenn er die folgenden Eigenschaften erfüllt:
 | 
						||
    1. Jede Seite enthält höchstens $2m$ Elemente.
 | 
						||
    2. Jede Seite, außer der Wurzelseite, enthält mindestens $m$ Elemente. 
 | 
						||
    3. Jede Seite ist entweder eine Blattseite ohne Nachfolger oder hat $i+1$ Nachfolger, falls $i$ die Anzahl ihrer Elemente ist.
 | 
						||
    4. Alle Blattseiten liegen auf der gleichen Stufe.
 | 
						||
 | 
						||
Eigenschaften des B-Baumes
 | 
						||
- $n$ Datensätze in der Hauptdatei => in $log_m(n)$ Seitenzugriffen von der Wurzel zum Blatt
 | 
						||
- Durch Balancierungskriterium wird Eigenschaft nahe an der vollständigen Ausgeglichenheit erreicht (1. Kriterium vollständig erfüllt, 2. Kriterium näherungsweise)
 | 
						||
- Kriterium garantiert 50% Speicherplatzausnutzung
 | 
						||
- einfache, schnelle Algorithmen zum Suchen, Einfügen und Löschen von Datensätzen (Komplexität von $O(log_m(n))$)
 | 
						||
 | 
						||
Suchen in B-Bäumen
 | 
						||
- lookup wie in statischen Indexverfahren
 | 
						||
- Startend auf Wurzelseite Eintrag im B-Baum ermitteln, der den gesuchten Zugriffsattributwert $w$ überdeckt => Zeiger verfolgen, Seite nächster Stufe laden
 | 
						||
- Suchen: 38, 20, 6
 | 
						||
- 
 | 
						||
 | 
						||
Einfügen in B-Bäumen
 | 
						||
- Einfügen eines Wertes _w_
 | 
						||
    - mit lookup entsprechende Blattseite suchen
 | 
						||
    - passende Seite $n<2m$ Elemente, _w_ einsortieren
 | 
						||
    - passende Seite $n=2m$ Elemente, neue Seite erzeugen,
 | 
						||
       - ersten _m_ Werte auf Originalseite
 | 
						||
       - letzten _m_ Werte auf neue Seite
 | 
						||
       - mittleres Element auf entsprechende Indexseite nach oben
 | 
						||
    - eventuell dieser Prozess rekursiv bis zur Wurzel
 | 
						||
 | 
						||
Einfügen in einen B-Baum: Beispiel
 | 
						||

 | 
						||

 | 
						||
 | 
						||
Löschen in B-Bäumen
 | 
						||
- bei weniger als $m$ Elementen auf Seite: Unterlauf
 | 
						||
- Löschen eines Wertes $w$ : Bsp.: 24; 28, 38, 35
 | 
						||
    - mit lookup entsprechende Seite suchen
 | 
						||
    - $w$ auf Blattseite gespeichert => Wert löschen, eventuell Unterlauf behandeln
 | 
						||
    - $w$ nicht auf Blattseite gespeichert => Wert löschen, durch lexikographisch nächstkleineres Element von einer Blattseite ersetzen, eventuell auf Blattseite => Unterlauf
 | 
						||
- Unterlaufbehandlung
 | 
						||
    - Ausgleichen mit der benachbarten Seite (benachbarte Seite $n$ Elemente mit $n>m$)
 | 
						||
    - oder Zusammenlegen zweier Seiten zu einer (Nachbarseite $n=m$ Elemente), das "mittlere" Element von Indexseite darüber dazu, auf Indexseite eventuell => Unterlauf
 | 
						||
- Einfügen des Elementes 22; Löschen von 22
 | 
						||
    
 | 
						||
 | 
						||
Komplexität der Operationen
 | 
						||
- Aufwand beim Einfügen, Suchen und Löschen im B-Baum immer $O(log_m(n))$ Operationen
 | 
						||
- entspricht genau der "Höhe" des Baumes
 | 
						||
- Konkret: Seiten der Größe 4 KB, Zugriffsattributwert 32 Bytes, 8-Byte-Zeiger: zwischen 50 und 100 Indexeinträge pro Seite; Ordnung dieses B-Baumes 50
 | 
						||
- 1.000.000 Datensätze: $log_{50}(1000000) = 4$ Seitenzugriffe im schlechtesten Fall
 | 
						||
- Wurzelseite jedes B-Baumes normalerweise im Puffer: 3 Seitenzugriffe
 | 
						||
 | 
						||
## B+-Baum
 | 
						||
Varianten
 | 
						||
- B+-Bäume: nur Blattebene enthält Daten -> Baum ist hohl
 | 
						||
- B∗-Bäume: Aufteilen von Seiten vermeiden durch "Shuffle"
 | 
						||
- Präfix-B-Bäume: Zeichenketten als Zugriffsattributwerte, nur Präfix indexieren
 | 
						||
 | 
						||
B+-Baum: Motivation
 | 
						||
- B-Baum: Wie/wo werden Nicht-Schlüsseldaten (Tupel, TIDs) gespeichert?
 | 
						||
       - Zusammen mit Schlüsseln in allen Knoten?
 | 
						||
       - Problem beim Traversieren des Baumes, z.B. bei Bereichsanfragen
 | 
						||
    
 | 
						||
 | 
						||
B+-Baum
 | 
						||
- in der Praxis am häufigsten eingesetzte Variante des B-Baumes:
 | 
						||
       - effizientere Änderungsoperationen, insb. Löschen
 | 
						||
       - Verringerung der Baumhöhe
 | 
						||
- Änderungen gegenüber B-Baum
 | 
						||
    - in inneren Knoten nur noch Zugriffsattributwert und Zeiger auf nachfolgenden Seite der nächsten Stufe; nur Blattknoten enthalten neben Zugriffsattributwert die Daten (Datensätze bzw. Verweise auf Datensätze in der Hauptdatei)
 | 
						||
    - Knoten der Blattebene sind untereinander verkettet für effiziente Unterstützung von Bereichsanfragen
 | 
						||
 | 
						||
B+-Baum: Aufbau
 | 
						||

 | 
						||
 | 
						||
Ordnung; Operationen
 | 
						||
- Ordnung für B+-Baum: $(x,y), x$ Mindestbelegung der Indexseiten, $y$ Mindestbelegung der Blattseiten
 | 
						||
- delete gegenüber B-Baum effizienter ("Ausleihen" eines Elementes von der Blattseite entfällt)
 | 
						||
- Zugriffsattributwerte in inneren Knoten können sogar stehenbleiben
 | 
						||
- häufig als Primärindex eingesetzt
 | 
						||
- B+-Baum ist dynamische, mehrstufige, indexsequenziellen Datei
 | 
						||
 | 
						||
B+-Baum: Blattebene mit Verweisen
 | 
						||

 | 
						||
 | 
						||
B+-Baum: Datenstrukturen
 | 
						||
```cpp
 | 
						||
BPlusBranchNode =record of
 | 
						||
    nkeys: int;
 | 
						||
    ptrs: array[0 .. nkeys ] of PageNum;
 | 
						||
    keys: array[1 .. nkeys ]of KeyType;
 | 
						||
    level: int;
 | 
						||
/* Level= 1 zeigt an, dass die ptrs auf Blätter zeigen */
 | 
						||
end;
 | 
						||
```
 | 
						||
```cpp
 | 
						||
BPlusLeafNode = record of
 | 
						||
    nkeys: int;
 | 
						||
    keys: array[1 .. nkeys ]of KeyType;
 | 
						||
    payload: array[1 .. nkeys ]of LoadType ; /* Daten bzw. TIDs */
 | 
						||
    nextleaf: PageNum;
 | 
						||
end;
 | 
						||
```
 | 
						||
 | 
						||
Operationen im B+-Baum
 | 
						||
- lookup: wie im B-Baum jedoch immer bis zur Blattebene
 | 
						||
- $search(u,o)$: Lookup für unteren Wert $u$ , Traversieren auf der Blattebene bis zum oberen Wert $o$
 | 
						||
- $insert$: ähnlich zum B-Baum
 | 
						||
    - im Fall des Split bei Überlaufbehandlung wird nur ein Separatorschlüssel im Elternknoten eingefügt
 | 
						||
    - z.B. Kopie des kleinsten Schlüsselwertes des "rechten" Kindknotens oder geeigneter Wert zwischen beiden Knoten
 | 
						||
- $delete$: ähnlich zum B-Baum, jedoch
 | 
						||
    - Löschen der Daten zunächst nur auf Blattebene
 | 
						||
    - bei Unterlauf: entweder Ausgleich mit Nachbarknoten oder Vereinigen mit Nachbarknoten; ggf. Anpassen (Ausgleich) oder Löschen (Vereinigen) des Separatorschlüssels
 | 
						||
    - alternativ: Unterlauf akzeptieren und durch spätere inserts oder Reorganisation auflösen
 | 
						||
 | 
						||
Weitere Entwurfsentscheidungen für B+-Baum
 | 
						||
- Schlüsselsuche im Knoten: sequenzielle Suche vs. binäre Suche vs. Interpolation Search
 | 
						||
- Knotengröße: Maximieren der Anzahl der Vergleiche (Knotennutzen) pro I/O-Zeiteinheit
 | 
						||
 | 
						||
| Seitengröße (KB) | Sätze/Seite | Knotennutzen | I/O-Zeit (ms) | Nutzen/Zeit |
 | 
						||
| ---------------- | ----------- | ------------ | ------------- | ----------- |
 | 
						||
| 4                | 143         | ≈7           | 5,020         | 1,427       |
 | 
						||
| 16               | 573         | ≈ 9          | 5,080         | 1,804       |
 | 
						||
| 64               | 2.294       | ≈ 11         | 5,320         | 2,098       |
 | 
						||
| 128              | 4.588       | ≈ 12         | 5,640         | 2,157       |
 | 
						||
| 256              | 9.175       | ≈ 13         | 6,280         | 2,096       |
 | 
						||
| 1024             | 36.700      | ≈ 15         | 10,120        | 1,498       |
 | 
						||
| 4096             | 146.801     | ≈ 17         | 25,480        | 0,674       |
 | 
						||
[Literatur](G. Graefe: Modern B-Tree Techniques, Foundations and Trends in Databases, 2010)
 | 
						||
 | 
						||
- Konsistenzprüfung während der Traversierung
 | 
						||
    - mögliche Inkonsistenzen durch Speicherfehler, konkurrierende Änderungen + Implementierungsfehler, ...
 | 
						||
- Cache-Optimierung: Knotenstruktur, Kompression (Präfix-/Suffix Truncation)
 | 
						||
- Pointer Swizzling: zur Ersetzung der logischen Seitennummern durch physische Adressen
 | 
						||
- Knotengröße: bei großen Puffern kann großer Teil der inneren Knoten im Puffer gehalten werden -> größere Knoten (im MB-Bereich) sinnvoll
 | 
						||
- Verzögern des Splits bei Überlaufbehandlung: Ausgleichen mit Nachbarknoten soweit möglich; verbessert Auslastung der Knoten
 | 
						||
- zusätzliche Verweise zwischen Knoten
 | 
						||
    - z.B. zwischen inneren Knoten der gleichen Ebene, zu Elternknoten, etc.
 | 
						||
    - ermöglichen Konsistenzchecks, aber erschweren Sperren für Nebenläufigkeitskontrolle
 | 
						||
 | 
						||
## Weitere Varianten
 | 
						||
Präfix-B-Baum
 | 
						||
- B-Baum über Zeichenkettenattribut
 | 
						||
    - lange Schlüssel in inneren Knoten -> hoher Speicherplatzbedarf
 | 
						||
    - vollständige Schlüssel eigentlich nicht notwendig, da nur "Wegweiser"
 | 
						||
- Idee: Verwaltung von Trennwerten -> Präfix-B-Baum
 | 
						||
    - in inneren Knoten nur Trennwerte, die lexikographisch zwischen den Werten liegen
 | 
						||
    - möglichst kurze Trennwerte, z.B. kürzester eindeutiger Präfix
 | 
						||
    
 | 
						||
    - aber: Beispiel "Vandenberg" und "Vandenbergh"
 | 
						||
 | 
						||
Mehr-Attribut-B-Baum
 | 
						||
- B-Baum ist eindimensionale Struktur, jedoch können mehrere Attribute als kompositer Schlüssel indexiert werden
 | 
						||
  `create index NameIdx on KUNDE(Name, Vorname)`
 | 
						||
- allerdings: Attribute werden bei partial-match-Anfragen nicht gleich behandelt!
 | 
						||
- Alternative: raumfüllende Kurven -> multidimensionale Indexstrukturen
 | 
						||
    
 | 
						||
    
 | 
						||
## Optimierungen für moderne Hardware
 | 
						||
Optimierungspotential
 | 
						||
- Verbesserung der Cache-Trefferrate
 | 
						||
    - Organisation der Datenstruktur entsprechend Cacheline (Größe, Anordnung der Daten)
 | 
						||
    - In-Place-Update im Hauptspeicher -> Cache-Invalidierung: verändertes Update Handling
 | 
						||
    - Pointer Swizzling
 | 
						||
- Berücksichtigung der Storage-Eigenschaften
 | 
						||
    - SSD vs. HDD
 | 
						||
    - Bevorzugung sequentieller Schreiboperationen
 | 
						||
    - spezielle Berücksichtigung für Main-Memory-Indexe
 | 
						||
- Synchronisation in Multicore-Umgebungen
 | 
						||
    - Lock/Latch-freie Operationen
 | 
						||
 | 
						||
 | 
						||
CSB+-Baum
 | 
						||
- =Cache Sensitive B+ Tree (Rao, Ross: Making B+-Trees Cache Conscious in Main Memory, SIGMOD 2000)
 | 
						||
- "Cache-Freundlichkeit" durch
 | 
						||
    1. Platzsparen im Knoten -> mehr relevante Daten im Cache
 | 
						||
    2. Eliminieren von Zeigern für 1. und Reduzierung von Zeigerarithmetik
 | 
						||
- Ansatz: veränderte Struktur der inneren Knoten
 | 
						||
    - Zeiger auf ersten Kindknoten
 | 
						||
    - alle Kindknoten eines Knotens sind in einem zusammenhängenden Speicherbereich (Knotengruppe) allokiert und werden über einenOffsetadressiert
 | 
						||
    
 | 
						||
 | 
						||
Bw-Baum
 | 
						||
- = Buzzword Tree (Levandoski, Lomet, Sengupta: The Bw-Tree: A B-tree for New Hardware Platforms, ICDE 2013)
 | 
						||
- Ziele: Cache-Freundlichkeit, Multicore-CPUs, Flash-Speichereigenschaften
 | 
						||
- Techniken
 | 
						||
    - überwiegend Latch-freie Operationen, stattdessen atomare CAS-Instruktionen
 | 
						||
    - spezifische Struktur-Modifikationsoperationen: Folge von atomaren Modifikationen, Blink-ähnliche Struktur
 | 
						||
    - Delta-Updates und Log Structured Storage (LSS)
 | 
						||
        - Änderungen an Seiten/Knoten werden nicht direkt ausgeführt, sondern in Delta-Records pro Knoten erfasst
 | 
						||
        - keine Synchronisation für Zugriff auf Seiten notwendig
 | 
						||
        - Seite kann auch nach Änderung im Cache verbleiben
 | 
						||
        - Seiten + Delta Records werden periodisch konsolidiert
 | 
						||
        - Log Structured Storage -> später!
 | 
						||
    - Mapping-Tabelle: logische Seitennummern in
 | 
						||
      - (a) Offset im Flash-Speicher
 | 
						||
      - (b) Zeiger im Hauptspeicher
 | 
						||
 | 
						||
## LSM-Baum
 | 
						||
- = Log Structured Merge Tree (O’Neil, Cheng, Gawlick, O’Neil: The log-structured merge-tree (LSM-tree). Acta Informatica. 33 (4): 351-385, 1996)
 | 
						||
- Ziel: höherer Schreibdurchsatz durch Eliminierung verstreuter In-Place-Updates
 | 
						||
- Einsatz in diversen NoSQL-Systemen: HBase, Cassandra, BigTable, LevelDB, RocksDB, ...
 | 
						||
- Grundidee
 | 
						||
    - Batches von Schreiboperationen werden sequentiell in Indexdateien gespeichert, d.h. Sortieren vor Schreiben auf Externspeicher (Log Structured)
 | 
						||
    - Neue Updates werden in neuen Indexdateien gespeichert
 | 
						||
    - Indexdateien werden periodisch zusammengefügt (Merge)
 | 
						||
    - Leseoperationen müssen alle Indexdateien konsultieren
 | 
						||

 | 
						||
 | 
						||
LSM-Baum: Realisierung
 | 
						||
- Main-Memory-Baum $C_0$ als Puffer, sortiert nach Schlüsseln, z.B. als AVL- oder RB-Baum
 | 
						||
  - bei Erreichen eines gegebenen Füllgrads -> Herausschreiben auf Disk (siehe unten)
 | 
						||
  - ergänzt um Write Ahead Logging auf Disk für Wiederherstellung nach Systemfehler
 | 
						||
- mehrere Append-only, disk-basierte Indexe $C_1, C_2,...,$ ebenfalls sortiert nach Schlüssel (z.B. als B-Baum)
 | 
						||
  - effiziente Unterstützung von Scans (Schlüsselsuche)
 | 
						||
  - Merge in einem Schritt
 | 
						||
- Aktualität der Indexe: $C_0,C_1,...,C_n$
 | 
						||
 | 
						||
LSM-Baum: Verdichtung
 | 
						||
- wenn bestimmte Anzahl von Dateien erzeugt wurden (z.B. 5 Dateien je 100 Datensätze), werden diese in eine Datei gemischt (1 Datei mit 500 Sätzen)
 | 
						||
- sobald 5 Dateien mit 500 Sätzen vorliegen, dann Mischen in eine Datei
 | 
						||
- usw.
 | 
						||
- Nachteil: große Anzahl von Dateien, die alle durchsucht werden müssen
 | 
						||
 | 
						||
LSM-Baum: Ebenenweise Verdichtung
 | 
						||
1. pro Ebene wird eine bestimmte Zahl von Dateien verwaltet, partitioniert nach Schlüsseln (keine Überlappung der Schlüssel) -> Suche nur in einer Datei notwendig
 | 
						||
2. Ausnahme: erste Ebene (Überlappung erlaubt)
 | 
						||
3. Mischen der Dateien jeweils in die nächsthöhere Ebene: Auswahl einer Datei und Aufteilen der Sätze -> Platz für neue Daten schaffen
 | 
						||
 | 
						||
LSM-Baum: Lesezugriffe
 | 
						||
- grundsätzlich: Verbesserung der Schreibperformance zulasten der Leseperformance
 | 
						||
  - Suche in allen Indexen $C_0,C_1,...,C_n$ notwendig
 | 
						||
- Vermeiden unnötiger Lesevorgänge durch Bloom-Filter pro Index oder pro Run
 | 
						||
  - (probabilistische) Datenstruktur zum Feststellen, ob Objekt in einer Menge enthalten ist
 | 
						||
  - Bit-Feld: Objekt wird über $k$ Hashfunktionen auf $k$ Bits abgebildet, die auf 1 gesetzt werden
 | 
						||
  - Prüfen auf Enthaltensein: Hashfunktionen anwenden -> Wenn alle $k$ Bits $= 1$, dann ist Objekt enthalten
 | 
						||
  - aber falsch-positive Werte möglich!
 | 
						||
  - können ebenfalls durch Mischen kombiniert werden
 | 
						||
  - [Quelle](Bloom: Space/Time Trade-offs in Hash Coding with Allowable Errors. CACM, 13(7):422-426, 1970)
 | 
						||
 | 
						||
## Zusammenfassung
 | 
						||
- B+-Baum als "Arbeitspferd" für Indexing
 | 
						||
- Standardoperationen: Suche, Einfügen, Löschen
 | 
						||
- noch nicht betrachtet: Nebenläufigkeitskontrolle und Wiederherstellung im Fehlerfall
 | 
						||
- diverse Varianten und Optimierungen
 | 
						||
- LSM-Baum für schreibintensive Workloads
 | 
						||
 | 
						||
# Hashing
 | 
						||
## Hashing
 | 
						||
- Zugriff über Adressberechnung aus Schlüssel
 | 
						||
- linearer Adressraum der Grösse $n$
 | 
						||
    - Adressierung in einem Array
 | 
						||
- Ziel: direkter Zugriff in $O(1)$ statt logarithmisch wie bei Bäumen
 | 
						||
 | 
						||
Hashverfahren
 | 
						||
- Schlüsseltransformation und Überlaufbehandlung
 | 
						||
- DB-Technik:
 | 
						||
    - Disk-basiert: Bildbereich entspricht Seiten-Adressraum
 | 
						||
    - Hauptspeicher: Adresse in einem Array (Hauptspeicheradresse plus Offset)
 | 
						||
- Dynamik: dynamische Hashfunktionen oder Re-Hashen
 | 
						||
 | 
						||
Grundprinzipien
 | 
						||
- Basis-Hashfunktion: $h(k)= k mod m$
 | 
						||
    - $m$ oft Primzahl da besseres Verhalten bei Kollisionen
 | 
						||
    - oder $m=2^k$ aufgrund einfacher Berechnungen
 | 
						||
- Überlauf-Behandlung
 | 
						||
    - Überlaufseiten als verkettete Liste
 | 
						||
    - lineares Sondieren
 | 
						||
    - quadratisches Sondieren
 | 
						||
    - doppeltes Hashen
 | 
						||
    - ...
 | 
						||
 | 
						||
Hashverfahren für blockorientierte Datenhaltung
 | 
						||

 | 
						||
 | 
						||
Operationen und Zeitkomplexität
 | 
						||
- lookup, modify, insert, delete
 | 
						||
- lookup benötigt maximal $1+ #B(h(w))$ Seitenzugriffe
 | 
						||
- $#B(h(w))$ Anzahl der Seiten (inklusive der Überlaufseiten) des Buckets für Hash-Wert $h(w)$
 | 
						||
- Untere Schranke 2 (Zugriff auf Hashverzeichnis plus Zugriff auf erste Seite)
 | 
						||
 | 
						||
Statisches Hashen: Probleme
 | 
						||
- mangelnde Dynamik
 | 
						||
- Vergrößerung des Bildbereichs erfordert komplettes Neu-Hashen
 | 
						||
- Wahl der Hashfunktion entscheidend; 
 | 
						||
  - Bsp.: Hash-Index aus 100 Buckets, Studenten über 6-stellige MATRNR (wird fortlaufend vergeben) hashen
 | 
						||
  - ersten beiden Stellen: Datensätze auf wenigen Seiten quasi sequenziell abgespeichert
 | 
						||
  - letzten beiden Stellen: verteilen die Datensätze gleichmäßig auf alle Seiten
 | 
						||
- Sortiertes Ausgeben einer Relation nicht unterstützt
 | 
						||
 | 
						||
## Hash-Funktionen
 | 
						||
- klassisch, etwa Divisions-Rest-Methode
 | 
						||
    $h() = x mod m$
 | 
						||
- zusammengesetzt, etwa $h(k)= h_2 (h_1 (k))$ (siehe später Spriralhashen)
 | 
						||
- ordnungserhaltend
 | 
						||
    $k_1 < k_2 => ( h(k_1) = h(k_2) \vee h(k_1) < h(k_2))$
 | 
						||
- dynamisch (siehe später)
 | 
						||
- mehrdimensional (siehe später)
 | 
						||
- materialisiert (etwa Dictionary Encoding, siehe später)
 | 
						||
 | 
						||
Ordnungserhaltenes Hashen
 | 
						||
- Schlüsselwerte werden als 8-Bit-Integer-Werte ohne Vorzeichen kodiert und sind gleichmässig im Bereich $0...2^8-1$ verteilt.
 | 
						||
- Die Extraktion der ersten drei Bits ergibt eine ordnungserhaltende Hashfunktion für den Bereich $0...2^3-1$.
 | 
						||
- Sind die Schlüsselwerte nicht gleichverteilt, etwa weil es sich um fortlaufend vergebene Nummern handelt, ist das Ergebnis zwar weiterhin ordnungserhaltend, aber die Hash-Tabelle ist sehr ungleichmäßig gefüllt.
 | 
						||
 | 
						||
## Hardware-sensitives Hashen
 | 
						||
Neue Hardware und Hash-Funktionen
 | 
						||
- Beobachtung: Hashen mit klassischem Sondieren ungünstig für neue Hardware
 | 
						||
  - schwer parallelisierbar
 | 
						||
  - Clustern von Werten verletzt Nähe der Werte (bei Cache Lines)
 | 
						||
- Varianten versuchen beide Punkte anzugehen
 | 
						||
    - Cuckoo-Hashing
 | 
						||
    - optimiertes lineares Sondieren
 | 
						||
    - Hopscotch-Hashing
 | 
						||
    - Robin-Hood-Hashing
 | 
						||
 | 
						||
Cuckoo-Hashen
 | 
						||
- Kuckucks-Hashen
 | 
						||
- soll Parallelität erhöhen im Vergleich zu linearem Sondieren
 | 
						||
- Idee: Zwei Tabellen mit zwei Hash-Funktionen
 | 
						||
    - im Fall einer Kollision in einer Tabelle wird in der zweiten Tabelle gesucht
 | 
						||
    - ist dort der Platz belegt, wird der dortige Eintrag verdrängt in die jeweils andere Tabelle
 | 
						||
          - _Kuckuck wirft Ei aus dem Nest_
 | 
						||
    - dies wird solange gemacht bis ein freier Platz gefunden wird
 | 
						||
- Beispiel
 | 
						||
  - zwei einfache Hash-Funktionen, die jeweils die letzte beziehungsweise vorletzte Dezimalstelle einer Zahl extrahieren
 | 
						||
      $h_1(k) = k mod 10$
 | 
						||
      $h_2(k) = (k/10) mod 10$
 | 
						||
  - Bei einer Suche muss immer in beiden Tabellen nachgeschaut werden, also $T_1[h_1(k)] = k\vee T_2[h_2(k)] = k$.
 | 
						||
  - Wir fügen die Zahlen $433, 129$ und $555$ in die Tabelle $T_1$ ein. Beim Einfügen von $783$ ist der Platz in Tabelle $T_1$ belegt, so dass diese Zahl in $T_2$ gespeichert werden muss. Wird nun mit $103$ eine weitere Zahl eingefügt, die mit $433$ unter $h_1$ kollidiert, ist dies mit $h_2$ weiterhin möglich.
 | 
						||
 | 
						||
Cuckoo Beispiel
 | 
						||
- Ergebnis des Einfügens von 433, 129 , 555 , 783 , 103
 | 
						||
|       | 0   | 1   | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9   |
 | 
						||
| ----- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
 | 
						||
| $T_1$ |     |     |     | 433 |     | 555 |     |     |     | 129 |
 | 
						||
| $T_2$ | 103 |     |     |     |     |     |     |     | 783 |
 | 
						||
- Wird nun die Zahl $889$ eingefügt, so sind beide möglichen Positionen belegt. $889$ kann in $T_1$ die dort stehende Zahl $129$ verdrängen, die in $T_2$ an der Position $T_2[2]$ gespeichert werden kann.
 | 
						||
|       | 0   | 1   | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9   |
 | 
						||
| ----- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
 | 
						||
| $T_1$ |     |     |     | 433 |     | 555 |     |     |     | 129 |
 | 
						||
| $T_2$ | 103 |     | 129 |     |     |     |     |     | 783 |
 | 
						||
- Wird nun $789$ eingefügt, sind wiederum beide Positionen belegt. Das Verdrängen von $889$ aus $T_1$ würde zu einem kaskadierenden Verdrängen führen: $889$ würde in $T_2$ dann $783$ verdrängen, das wiederum $433$ in $T_1$ verdrängen würde. Dies würde gehen da $433$ an der Stelle $T_2[3]$ Platz hätte
 | 
						||
|       | 0   | 1   | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9   |
 | 
						||
| ----- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
 | 
						||
| $T_1$ |     |     |     | 783 |     | 555 |     |     |     | 129 |
 | 
						||
| $T_2$ | 103 |     | 129 | 433 |     |     |     |     | 783 |
 | 
						||
 | 
						||
Weitere Prinzipien der Optimierung
 | 
						||
- Lokalität von Datenzugriffen verringert die Wahrscheinlichkeit von Cache Misses
 | 
						||
- Blockung von Daten kann an die Grösse von Cache-Lines (64 Bytes) angepasst werden, und erhöht den Durchsatz
 | 
						||
- Parallelisierung für SIMD basierend auf einer Vektorisierung der Daten kann insbesondere SIMD-basierte Co-Prozessoren gut ausnutzen, aber greift auch bei MICs
 | 
						||
 | 
						||
Optimiertes lineares Sondieren
 | 
						||
- Lineares Sondieren ist gut geeignet, um den Sondierungsvorgang auf Vektoren zu parallelisieren
 | 
						||
  - Suchschlüssel kann in einen Vektor der Länge $n$ an alle Positionen kopiert werden
 | 
						||
  - beginnend ab derem initialen Sondierungspunkt $h(k)$ können dann Vektoren jeweils mit Vektoren aus der Hash-Tabelle verglichen werden, also zuerst mit
 | 
						||
    $H[h)k),...,h(k)+n - 1]$, dann mit $H[h(k) + n,...,h(k) + 2n-1 ]$, etc.
 | 
						||
  - Vergleich kann parallel erfolgen; muss sowohl auf Vorhandensein von $k$ als auch auf Existenz einer leeren Position prüfen
 | 
						||
 | 
						||
Hopscotch-Hashen
 | 
						||
- Hopscotch: _Himmel und Hölle_ beziehungsweise _wild herumhopsen_
 | 
						||
- Begrenzung des Sondierungsraum auf eine (konstante) Länge
 | 
						||
- Idee:
 | 
						||
    - beim Einfügen erfolgt die Suche (parallel) in der festen Nachbarschaft
 | 
						||
    - wird Schlüssel $k$ nicht gefunden und existiert kein freier Slot in der festen Nachbarschaft, dann wird versucht, $k$ mit einem anderen Schlüssel aus der festen Nachbarschaft zu tauschen
 | 
						||
    - dafür wird die nächste freie Stelle gesucht; von dieser wird rückwärts in Richtung $h(k)$ gesucht und jeder Eintrag $k′$ untersucht
 | 
						||
    - wenn die aktuelle freie Stelle noch in der festen Nachbarschaft von $k′$ liegt, wird getauscht: $k′$ springt auf die freie Stelle
 | 
						||
 | 
						||
Robin-Hood-Hashen
 | 
						||
- Robin-Hood: _Nimm von den Reichen gib es den Armen_
 | 
						||
- Basisidee: in der Situation, dass beim Sondieren für $k$ ein Platz bereits mit einem Element $k′$ besetzt ist, wird der nächste Sondierungsschritt mit demjenigen Element weitergeführt, das die kleinere Distanz zum eigentlichen Hash-Wert $h(k)$ bzw. $h(k′)$ hat
 | 
						||
 | 
						||
## Dynamische Hash-Verfahren
 | 
						||
Lineares Hashen
 | 
						||
- Folge von Hash-Funktionen, die wie folgt charakterisiert sind:
 | 
						||
  - $h_i$: dom(Primärschlüssel) ->$\{0,..., 2^i \times N\}$ ist eine Folge von Hash-Funktionen mit $i\in\{0,1,2,...\}$ und $N$ als Anfangsgröße des Hash-Verzeichnisses
 | 
						||
  - Wert von $i$ wird auch als Level der Hash-Funktion bezeichnet 
 | 
						||
  - $dom(Primärschlüssel)$ wird im folgenden als $dom(Prim)$ abgekürzt
 | 
						||
- Für diese Hash-Funktionen gelten die folgenden Bedingungen:
 | 
						||
    - $h_{i+1}(w) = h_i(w)$ für etwa die Hälfte aller $w\in dom(Prim)$
 | 
						||
    - $h_{i+1}(w) = h_i(w) + 2^i\times N$ für die andere Hälfte
 | 
						||
  - Bedingungen sind zum Beispiel erfüllt, wenn $h_i(w)$ als $w mod(2^i\times N)$ gewählt wird
 | 
						||
  - Darstellung durch Bit-Strings, Hinzunahme eines Bits verdoppelt Bildbereich
 | 
						||
 | 
						||
Prinzip lineares Hashen
 | 
						||
- für ein $w$ höchstens zwei Hash-Funktionen zuständig, deren Level nur um 1 differiert, Entscheidung zwischen diesen beiden durch Split-Zeiger
 | 
						||
  - $sp$ Split-Zeiger (gibt an, welche Seite als nächstes geteilt wird)
 | 
						||
  - $lv$ Level (gibt an, welche Hash-Funktionen benutzt werden)
 | 
						||
- Aus Split-Zeiger und Level läßt sich die Gesamtanzahl $Anz$ der belegten Seiten wie folgt berechnen:
 | 
						||
  - $Anz = 2^{lv} + sp$
 | 
						||
- Beide Werte werden am Anfang mit 0 initialisiert.
 | 
						||

 | 
						||
 | 
						||
Lookup
 | 
						||
- $$s := h_{lv}(w)$;
 | 
						||
- if $s < sp$
 | 
						||
- then $s := h_{lv + 1}(w)$;
 | 
						||
- zuerst Hash-Wert mit der "kleineren" Hash-Funktion bestimmen
 | 
						||
- liegt dieser unter dem Wert des Split-Zeigers => größere Hash-Funktion verwenden
 | 
						||
 | 
						||
Splitten einer Seite
 | 
						||
1. Die Sätze der Seite (Bucket), auf die $sp$ zeigt, werden mittels $h_{lv+1}$ neu verteilt (ca. die Hälfte der Sätze wird auf Seite (Bucket) unter Hash-Nummer $2^{lv}*N +sp$ verschoben)
 | 
						||
2. Der Split-Zeiger wird weitergesetzt: $sp:=sp +1;$
 | 
						||
3. Nach Abarbeiten eines Levels wird wieder bei Seite 0 begonnen; der Level wird um 1 erhöht:
 | 
						||
 | 
						||
```
 | 
						||
if sp = 2^{lv} * N then
 | 
						||
    begin
 | 
						||
        lv := lv + 1 ;
 | 
						||
        sp := 0
 | 
						||
    end;
 | 
						||
```
 | 
						||

 | 
						||
 | 
						||
 | 
						||
Problem lineares Hashen
 | 
						||

 | 
						||
 | 
						||
Erweiterbares Hashen
 | 
						||
- Problem: Split erfolgt an fester Position, nicht dort wo Seiten überlaufen
 | 
						||
- Idee: binärer Trie zum Zugriff auf Indexseiten
 | 
						||
- Blätter unterschiedlicher Tiefe
 | 
						||
    - Indexseiten haben Tiefenwert
 | 
						||
    - Split erfolgt bei Überlauf
 | 
						||
- aber: Speicherung nicht als Trie, sondern als Array
 | 
						||
    - entspricht vollständigem Trie mit maximaler Tiefe
 | 
						||
       - "shared" Seiten als Blätter
 | 
						||
    - Array der Grösse 2 _d_ für maximale Tiefe _d_
 | 
						||
       - erfordert nun nur einen Speicherzugriff!
 | 
						||
    - bei Überlauf: Indexgrösse muss möglicherweise verdoppelt werden!
 | 
						||
- Ausgangslage:
 | 
						||
    - Einfügen von 00111111 würde Überlauf bei erreichter maximaler Tiefe erzeugen
 | 
						||
        
 | 
						||
    - Verdopplung der Indexgrösse
 | 
						||
        
 | 
						||
    - nun möglich: Split der Seite
 | 
						||
        
 | 
						||
 | 
						||
Variante: Array als Trie gespeichert
 | 
						||

 | 
						||
 | 
						||
Spiral-Hashen
 | 
						||
- Problem: zyklisch erhöhte Wahrscheinlichkeit des Splittens
 | 
						||
- Lösung: unterschiedliche Dichte der Hashwerte
 | 
						||
    - Interpretation der Bit-Strings als binäre Nachkommadarstellung einer Zahl zwischen $0.0$ und $1.0$
 | 
						||
    - Funktion von $[0.0,1.0] -> [0.0,1.0]$ so dass Dichte gleichmässig verteilter Werte nahe $1.0$ doppelt so gross ist wie nahe $0.0$
 | 
						||
- Umverteilung mittels Exponentialfunktion
 | 
						||
- Funktion $exp(n)$   $exp(n) = 2^n - 1$ erfüllt die Bedingungen
 | 
						||
- insbesondere gilt $2^0 - 1 = 0$ und $2^1 - 1 = 1$
 | 
						||
- Hashfunktion exhash
 | 
						||
    $exhash(k) = exp(h(k)) = 2^{h(k)} - 1$
 | 
						||
- Wirkung der verwendeten Hashfunktion im Intervall $0.0$ bis $1.0$
 | 
						||
    | $n$   | $2^n-1$     |
 | 
						||
    | ----- | ----------- |
 | 
						||
    | $0.0$ | $0.0$       |
 | 
						||
    | $0.1$ | $0.0717735$ |
 | 
						||
    | $0.2$ | $0.1486984$ |
 | 
						||
    | $0.3$ | $0.2311444$ |
 | 
						||
    | $0.4$ | $0.3195079$ |
 | 
						||
    | $0.5$ | $0.4142136$ |
 | 
						||
    | $0.6$ | $0.5157166$ |
 | 
						||
    | $0.7$ | $0.6245048$ |
 | 
						||
    | $0.8$ | $0.7411011$ |
 | 
						||
    | $0.9$ | $0.866066$  |
 | 
						||
    | $1.0$ | $1.0$       |
 | 
						||
- Spiralförmiges Ausbreiten
 | 
						||
    - Ausgangslage: 4 Seiten der Tiefe 2
 | 
						||
    
 | 
						||
- Spiralförmiges Ausbreiten
 | 
						||
    - Split der Seite mit der höchsten Dichte
 | 
						||
    - Ergebnis: 5 Seiten, davon 3 der Tiefe 2 und 2 der Tiefe 3
 | 
						||
    
 | 
						||
 | 
						||
## Grid-File
 | 
						||
Grid-Files
 | 
						||
- bekannteste und von der Technik her attraktive mehrdimensionale Dateiorganisationsform
 | 
						||
- eigene Kategorie: Elemente der Schlüsseltransformation wie bei Hashverfahren und Indexdateien wie bei Baumverfahren kombiniert
 | 
						||
  - deshalb hier bei Hash-Verfahren betrachtet
 | 
						||
 | 
						||
Grid-File: Zielsetzungen
 | 
						||
- Prinzip der 2 Plattenzugriffe: Jeder Datensatz soll bei einer _exact-match_ -Anfrage in 2 Zugriffen erreichbar sein
 | 
						||
- Zerlegung des Datenraums in Quader: _n_ -dimensionale Quader bilden die Suchregionen im Grid-File
 | 
						||
- Prinzip der Nachbarschaftserhaltung: Ähnliche Objekte sollten auf der gleichen Seite gespeichert werden
 | 
						||
- Symmetrische Behandlung aller Raum-Dimensionen: _partial-match_ -Anfragen ermöglicht
 | 
						||
- Dynamische Anpassung der Grid-Struktur beim Löschen und Einfügen
 | 
						||
 | 
						||
Prinzip der zwei Plattenzugriffe
 | 
						||
- bei exact-match
 | 
						||
    1. gesuchtes $k$ -Tupel auf Intervalle der Skalen abbilden; als Kombination der ermittelten Intervalle werden Indexwerte errechnet; Skalen im Hauptspeicher => noch kein Plattenzugriff
 | 
						||
    2. über errechnete Indexwerte Zugriff auf das _Grid-Directory_ ; dort Adressen der Datensatz-Seiten gespeichert; erster _Plattenzugriff_.
 | 
						||
    3. Der Datensatz-Zugriff: zweiter _Plattenzugriff_.
 | 
						||
 | 
						||
Aufbau eines Grid-Files
 | 
						||

 | 
						||
- Grid: $k$ eindimensionale Felder (Skalen), jede Skala repräsentiert Attribut
 | 
						||
- _Skalen_ bestehen aus Partition der zugeordneten Wertebereiche in Intervalle
 | 
						||
- Grid-Directory besteht aus Grid-Zellen, die den Datenraum in Quader zerlegen
 | 
						||
- Grid-Zellen bilden eine Grid-Region, der genau eine Datensatz-Seite zugeordnet wird
 | 
						||
- Grid-Region: $k$-dimensionales, konvexes (Regionen sind paarweise disjunkt)
 | 
						||
 | 
						||
Operationen
 | 
						||
- Zu Anfang: Zelle = Region = eine Datensatz-Seite
 | 
						||
- Seitenüberlauf:
 | 
						||
    - Seite wird geteilt
 | 
						||
    - falls zugehörige Gridregion aus nur einer Gridzelle besteht, muss ein Intervall auf einer Skala in zwei Intervalle unterteilt werden
 | 
						||
    - besteht Region aus mehreren Zellen, so werden diese Zellen in einzelne Regionen zerlegt
 | 
						||
- Seitenunterlauf:
 | 
						||
    - Zwei Regionen zu einer zusammenfassen, falls das Ergebnis eine neue, konvexe Region ergibt
 | 
						||
 | 
						||
Beispiel
 | 
						||
- Start-Grid-File
 | 
						||
    
 | 
						||
    - Datensätze einfügen: $(45,D),(2,B),(87,S),(75,M),(55,K),(3,Y),(15,D),(25,K),(48,F)$
 | 
						||
    - jede Seite des Grid-Files fasst bis zu drei Datensätze
 | 
						||
- Eingefügt: $(45, D), (2, B), (87, S)$
 | 
						||
    
 | 
						||
- Einfügen von $(75, M)$ erzwingt Split
 | 
						||
    
 | 
						||
- Eingefügt: $(55, K)$
 | 
						||
    
 | 
						||
- Einfügen von $(3, Y)$ erzwingt wiederum einen Split
 | 
						||
    
 | 
						||
- Eingefügt: (15, D), (25, K),
 | 
						||
    
 | 
						||
- Einfügen von (48, F) erzwingt wiederum einen Split
 | 
						||
    
 | 
						||
 | 
						||
 | 
						||
Buddy-System
 | 
						||
- Beschriebenes Verfahren: Buddy-System (Zwillings-System)
 | 
						||
- Die im gleichen Schritt entstandenen Zellen können zu Regionen zusammengefasst werden; Keine andere Zusammenfassung von Zellen ist im Buddy-System erlaubt
 | 
						||
- Unflexibel beim Löschen: nur Zusammenfassungen von Regionen erlaubt, die vorher als Zwillinge entstanden waren
 | 
						||
- Beispiel: $(15,D)$ löschen: Seiten 1 und 4 zusammenfassen; $(87,S)$ löschen, Seite 2 zwar unterbelegt, kann aber mit keiner anderen Seite zusammengefasst werden
 | 
						||
 | 
						||
# Weitere Indexstrukturen
 | 
						||
## Bitmap-Indexe
 | 
						||
Bitmap-Indexe
 | 
						||
- Idee: _Bit-Vektor_ zur Kodierung der Tupel-Attributwert-Zuordnung
 | 
						||
- Vergleich mit baumbasierten Indexstrukturen:
 | 
						||
    - vermeidet degenerierte B-Bäume
 | 
						||
    - unempfindlicher gegenüber höherer Zahl von Attributen
 | 
						||
    - einfachere Unterstützung von Anfragen, in denen nur einige (der indexierten) Attribute beschränkt werden
 | 
						||
    - dafür aber i.allg. höhere Aktualisierungskosten
 | 
						||
       - beispielsweise in Data Warehouses wegen des überwiegend lesenden Zugriffs unproblematisch
 | 
						||
 | 
						||
Bitmap-Index: Realisierung
 | 
						||
- Prinzip: Ersetzung der TIDs (rowid) für einen Schlüsselwert im $b$ +-Baum durch Bitvektor
 | 
						||
- Knotenaufbau: $|B: 0 1001 0...0 1 | F: 1 01 000... 10  | O: 000 101 ...00 |$
 | 
						||
- Vorteil: geringerer Speicherbedarf
 | 
						||
    - Beispiel: 150.000 Tupel, 3 verschiedene Schlüsselwerte, 4 Byte für TID
 | 
						||
          - B+-Baum: 600 KB
 | 
						||
          - Bitmap: $3*18750 Byte =56KB$
 | 
						||
- Nachteil: Aktualisierungsaufwand
 | 
						||
- Definition in Oracle
 | 
						||
    ```sql
 | 
						||
    CREATE BITMAP INDEX bestellstatus_idx ON bestellung(status);
 | 
						||
    ```
 | 
						||
- Speicherung in komprimierter Form
 | 
						||
 | 
						||
Standard-Bitmap-Index
 | 
						||
- jedes Attribut wird getrennt abgespeichert
 | 
						||
- für jede Ausprägung eines Attributs wird ein
 | 
						||
    Bitmap-Vektor angelegt:
 | 
						||
       - für jedes Tupel steht ein Bit, dieses wird auf 1 gesetzt, wenn das indexierte Attribut in dem Tupel den Referenzwert dieses Bitmap-Vektors enthält
 | 
						||
       - die Anzahl der entstehenden Bitmap-Vektoren pro Dimension entspricht der Anzahl der unterschiedlichen Werte, die für das Attribut vorkommen
 | 
						||
- Beispiel: Attribut Geschlecht
 | 
						||
    - 2 Wertausprägungen (m/w)
 | 
						||
    - 2 Bitmap-Vektoren
 | 
						||
    | PersId | Name | Geschlecht | Bitmap-w | Bitmap-m |
 | 
						||
    | --- | --- | --- | --- | --- |
 | 
						||
    007 |James Bond |M |0 |1
 | 
						||
    008 |Amelie Lux |W |1 |0
 | 
						||
    010 |Harald Schmidt |M |0 |1
 | 
						||
    011 |Heike Drechsler |W |1 |0
 | 
						||
- Selektion von Tupeln kann nun durch entsprechende Verknüpfung von Bitmap-Vektoren erfolgen
 | 
						||
- Beispiel: Bitmap-Index über Attribute Geschlecht und Geburtsmonat
 | 
						||
  - (d.h. 2 Bitmap-Vektoren B-w und B-m für Geschlecht und 12 Bitmap-Vektoren B-1, ..., B-12 für die Monate, wenn alle Monate vorkommen)
 | 
						||
- Anfrage: "alle im März geborenen Frauen"
 | 
						||
    - Berechnung: $B-w \wedge B-3$ (bitweise konjunktiv verknüpft)
 | 
						||
    - Ergebnis: alle Tupel, an deren Position im Bitmap-Vektor des Ergebnis eine 1 steht
 | 
						||
 | 
						||
Mehrkomponenten-Bitmap-Index
 | 
						||
- bei Standard-Bitmap-Indexe entstehen für Attribute mit vielen Ausprägungen sehr viele Bitmap-Vektoren
 | 
						||
- $<n,m>$-Mehrkomponenten-Bitmap-Indexe erlauben $n*m$ mögliche Werte durch $n+m$ Bitmap-Vektoren zu
 | 
						||
    indexieren
 | 
						||
- jeder Wert $x(0\leq x\leq n*m-1)$ kann durch zwei Werte $y$ und $z$ repräsentiert werden:
 | 
						||
    $x=n*y+z$ mit $0\leq y\leq m-1$ und $0\leq z\leq n-1$
 | 
						||
    - dann nur noch maximal $m$ Bitmap-Vektoren für $y$ und $n$ Bitmap-Vektoren für $z$
 | 
						||
    - Speicheraufwand reduziert sich von $n*m$ auf $n+m$
 | 
						||
    - dafür müssen für eine Punktanfrage aber 2 Bitmap-Vektoren gelesen werden
 | 
						||
- Beispiel: Zweikomponenten-Bitmap-Index
 | 
						||
    - Für $M= 0 ..11$ etwa $x= 4*y + z$
 | 
						||
    - y-Werte: $B-2-1, B-1-1, B-0-1$
 | 
						||
    - z-Werte: $B-3-0, B-2-0, B-1-0, B-0-0$
 | 
						||
    | x|  y | || z |||||
 | 
						||
    | --- | --- | --- | --- | --- | --- | --- | --- |
 | 
						||
    | M | B-2-1 | B-1-1 | B-0-1| B-3-0| B-2-0| B-1-0| B-0-0
 | 
						||
    |5| 0 |1 |0 |0 |0 |1 |0
 | 
						||
    |3| 0 |0 |1 |1 |0 |0 |0
 | 
						||
    |0| 0 |0 |1 |0 |0 |0 |1
 | 
						||
    |11| 1| 0 |0| 1 |0| 0 |0
 | 
						||
 | 
						||
Beispiel: Postleitzahlen
 | 
						||
- Kodierung von Postleitzahlen
 | 
						||
- Werte 00000 bis 99999
 | 
						||
- direkte Umsetzung: 100.000 Spalten
 | 
						||
- Zweikomponenten-Bitmap-Index (erste 2 Ziffern + 3 letzte Ziffern): 1.100 Spalten
 | 
						||
- Fünf Komponenten: 50 Spalten
 | 
						||
    - geeignet für Bereichsanfragen "PLZ 39***"
 | 
						||
- Binärkodiert (bis $2^17$): 34 Spalten
 | 
						||
    - nur für Punktanfragen!
 | 
						||
- Bemerkung: Kodierung zur Basis 3 ergibt sogar nur 33 Spalten...
 | 
						||
 | 
						||
## Indexierung von Texten
 | 
						||
Indexierung von Texten
 | 
						||
- bisher vorgestellte Verfahren unterstützen prinzipiell auch die Indexierung von Zeichenketten
 | 
						||
- Probleme bereitet folgendes:
 | 
						||
    - unterschiedliche Längen der Zeichenketten als Suchschlüssel
 | 
						||
    - bei Sätzen: Zugriff auf einzelne Wörter bevorzugt
 | 
						||
    - Ähnlichkeiten u.a. über gemeinsame Präfixe und Editierabstand
 | 
						||
 | 
						||
Digital- und Präfixbäume
 | 
						||
- B-Bäume: Problem bei zu indexierenden Zeichenketten
 | 
						||
- Lösung: Digital- oder Präfixbäume
 | 
						||
- Digitalbäume indexieren (fest) die Buchstaben des zugrundeliegenden Alphabets
 | 
						||
- können dafür unausgeglichen werden
 | 
						||
- Beispiele: Tries, Patricia-Bäume
 | 
						||
- Präfixbäume indexieren Präfix der Zeichenketten
 | 
						||
 | 
						||
Tries
 | 
						||
- von "Information Retrieval", aber wie try gesprochen
 | 
						||
    - Abgrenzung vom _tree_ für allgemeine Suchbäume
 | 
						||
    
 | 
						||
- Knoten eines Tries
 | 
						||
    
 | 
						||
    - Probleme: lange gemeinsame Teilworte, nicht vorhandenen Buchstaben und Buchstabenkombinationen, möglicherweise leere Knoten, sehr unausgeglichene Bäume
 | 
						||
 | 
						||
Patricia-Bäume
 | 
						||
- Tries: Probleme bei Teilekennzahlen, Pfadnamen, URLs (lange gemeinsame Teilworte)
 | 
						||
- Lösung: Practical Algorithm To Retrieve Information Coded In Alphanumeric (Patricia)
 | 
						||
- Prinzip: Überspringen von Teilworten
 | 
						||
- Problem: Datenbanksprache bei Suchbegriff _Triebwerksperre_
 | 
						||
 | 
						||
Patricia-Baum und Trie im Vergleich
 | 
						||

 | 
						||
- übersprungene Teilworte zusätzlich speichern: Präfix-Bäume
 | 
						||
 | 
						||
 | 
						||
Präfix-Bäume
 | 
						||
- Patricia-Baum plus Abspeicherung der übersprungenen Präfixe
 | 
						||
    
 | 
						||
 | 
						||
Invertierte Listen
 | 
						||
- indizierte Worte (Zeichenketten) bilden eine lexikographisch sortierte Liste
 | 
						||
- einzelner Eintrag besteht aus einem _Wort_ und einer Liste von Dokument-Identifikatoren derjenigen Dokumente, in denen das Wort vorkommt
 | 
						||
- zusätzlich können weitere Informationen für die Wort-Dokument-Kombination abgespeichert werden:
 | 
						||
    - Position des (ersten Auftretens des) Wortes im Text
 | 
						||
    - Häufigkeit des Wortes im Text
 | 
						||
 | 
						||
Invertierte Listen
 | 
						||
    
 | 
						||
 | 
						||
## Mehrdimensionale Speichertechniken
 | 
						||
Mehrdimensionale Speichertechniken
 | 
						||
- bisher: eindimensional (keine partial-match-Anfragen, nur lineare Ordnung)
 | 
						||
- jetzt: mehrdimensional (auch partial-match-Anfragen, Positionierung im mehrdimensionalen Datenraum)
 | 
						||
- $k$ Dimensionen = $k$ Attribute können gleichberechtigt unterstützt werden
 | 
						||
- dieser Abschnitt
 | 
						||
    - mehrdimensionaler B-Baum
 | 
						||
    - mehrdimensionales Hashverfahren
 | 
						||
 | 
						||
## Mehrdimensionale Baumverfahren
 | 
						||
Mehrdimensionale Baumverfahren
 | 
						||
- KdB-Baum ist B+-Baum, bei dem Indexseiten als binäre Bäume mit Zugriffsattributen, Zugriffsattributwerten und Zeigern realisiert werden
 | 
						||
- Varianten von $k$ -dimensionalen Indexbäumen:
 | 
						||
    - _kd-Baum_ von Bentley und Friedman: für Hauptspeicheralgorithmen entwickelte, mehrdimensionale Grundstruktur (binärer Baum)
 | 
						||
    - _KDB-Baum_ von Robinson: Kombination kd-Baum und B-Baum ( $k$ -dimensionaler Indexbaum bekommt höheren Verzweigungsgrad)
 | 
						||
    - _KdB-Baum_ von Kuchen: Verbesserung des Robinson-Vorschlags, wird hier behandelt
 | 
						||
- KdB-Baum kann Primär- und mehrere Sekundärschlüssel gleichzeitig unterstützen
 | 
						||
- macht als Dateiorganisationsform zusätzliche Sekundärindexe überflüssig
 | 
						||
 | 
						||
Definition KdB-Baum
 | 
						||
- Idee: auf jeder Indexseite einen Teilbaum darstellen, der nach mehreren Attributen hintereinander verzweigt
 | 
						||
    - _KdB-Baum vom Typ_ ( $b$ , $t$ ) besteht aus
 | 
						||
        - inneren Knoten (Bereichsseiten) die einen _kd-Baum_ mit maximal $b$ internen Knoten enthalten
 | 
						||
        - Blättern (Satzseiten) die bis zu $t$ Tupel der gespeicherten Relation speichern können
 | 
						||
    - Bereichsseiten: _kd-Baum_ enthalten mit Schnittelementen und zwei Zeigern
 | 
						||
        - Schnittelement enthält Zugriffsattribut und Zugriffsattributwert; 
 | 
						||
        - linker Zeiger: kleinere Zugriffsattributwerte; 
 | 
						||
        - rechter Zeiger: größere Zugriffsattributwerte
 | 
						||
 | 
						||
Beispiel
 | 
						||

 | 
						||
 | 
						||
KdB-Baum: Struktur
 | 
						||
- Bereichsseiten
 | 
						||
    - Anzahl der Schnitt- und Adressenelemente der Seite
 | 
						||
    - Zeiger auf Wurzel des in der Seite enthaltenen kd-Baumes
 | 
						||
    - Schnitt- und Adressenelemente.
 | 
						||
- Schnittelement
 | 
						||
    - Zugriffsattribut
 | 
						||
    - Zugriffsattributwert
 | 
						||
    - zwei Zeiger auf Nachfolgerknoten des kd-Baumes dieser Seite (können Schnitt- oder Adressenelemente sein)
 | 
						||
- Adressenelemente: Adresse eines Nachfolgers der Bereichsseite im KdB-Baum (Bereichs- oder Satzseite)
 | 
						||
 | 
						||
KdB-Baum: Operationen
 | 
						||
- Komplexität $lookup$, $insert$ und $delete$ bei exact-match $O(log n)$
 | 
						||
- bei partial-match besser als $O(n)$
 | 
						||
- bei $t$ von $k$ Attributen in der Anfrage spezifiziert: Zugriffskomplexität von $O(n^{1-t/k})$
 | 
						||
 | 
						||
KdB-Baum: Trennattribute
 | 
						||
- Reihenfolge der Trennattribute
 | 
						||
    - entweder zyklisch festgelegt
 | 
						||
    - oder Selektivitäten einbeziehen: Zugriffsattribut mit hoher Selektivität sollte früher und häufiger als Schnittelement eingesetzt werden
 | 
						||
- Trennattributwert: aufgrund von Informationen über Verteilung von Attributwerten eine geeignete "Mitte" eines aufzutrennenden Attributwertebereichs ermitteln
 | 
						||
 | 
						||
 | 
						||
KdB-Baum: Brickwall
 | 
						||

 | 
						||
 | 
						||
## Mehrdimensionales Hashen
 | 
						||
Mehrdimensionales Hashen
 | 
						||
- Idee: Bit Interleaving
 | 
						||
- abwechselnd von verschiedenen Zugriffsattributwerten die Bits der Adresse berechnen
 | 
						||
- Beispiel: zwei Dimensionen
 | 
						||
    | | *0*0 | *0*1 | *1*0| *1*1|
 | 
						||
    | --- | --- | --- | --- | --- |
 | 
						||
    0*0*| 0000 |0001 |0100 |0101
 | 
						||
    0*1*| 0010 |0011 |0110 |0111
 | 
						||
    1*0*| 1000 |1001 |1100 |1101
 | 
						||
    1*1*| 1010 |1011 |1110 |1111
 | 
						||
 | 
						||
MDH von Kuchen
 | 
						||
- MDH baut auf linearem Hashen auf
 | 
						||
- Hash-Werte sind Bitfolgen, von denen jeweils ein Anfangsstück als aktueller Hashwert dient
 | 
						||
- je ein Bitstring pro beteiligtem Attribut berechnen
 | 
						||
- Anfangsstücke nun nach dem Prinzip des Bit-Interleaving zyklisch abarbeiten
 | 
						||
- Hashwert reihum aus den Bits der Einzelwerte zusammensetzen
 | 
						||
 | 
						||
MDH formal
 | 
						||
- mehrdimensionaler Wert $x := (x_1,..., x_k)\in D = D_1\times ...\times D_k$
 | 
						||
- Folge von mit _i_ indizierten Hashfunktionen konstruieren
 | 
						||
- _i_-te Hashfunktion $h_i(x)$ wird mittels Kompositionsfunktion $\bar{h}_i$ aus den jeweiligen $i$ -ten Anfangsstücken der lokalen Hash-Werte $h_{i_j}(x_j)$ zusammengesetzt: $h_i(x)=\bar{h}_i(h_{i_1}(x_1),...,h_{i_k}(x_k))$
 | 
						||
- lokale Hashfunktionen $h_{i_j}$ ergeben jeweils Bitvektor der Länge $z_{i_j} +1$:$h_{i_j} : D_j\rightarrow \{0,..., z_{i_j}\}, j\in\{1 ,..., k\}$
 | 
						||
- $z_{i_j}$ sollten möglichst gleich groß sein, um die Dimensionen gleichmäßig zu berücksichtigen
 | 
						||
- Kompositionsfunktion $\bar{h}_i$ setzt lokale Bitvektoren zu einem Bitvektor der Länge $i$ zusammen: $\bar{h}_i:\{0,...,z_{i1}\times ...\times\{0,..., z_{i_k}\}\rightarrow\{0,...,2^{i+1}-1\}$
 | 
						||
- ausgeglichene Länge der $z_{i_j}$ wird durch folgende Festlegung bestimmt, die Längen zyklisch bei jedem Erweiterungsschritt an einer Stelle erhöht:
 | 
						||
    $$z_{i_j} = \begin{cases} 2^{\lfloor \frac{i}{k}\rfloor +1} -1 \quad\text{ für } j-1\leq (i mod k)\\ 2^{\lfloor \frac{i}{k}\rfloor} -1 \quad\text{ für } j-1 > (i mod k) \end{cases}$$
 | 
						||
- Kompositionsfunktion:
 | 
						||
    $$\bar{h}_i(x)=\sum_{r=0}^i (\frac{(x_{(r mod k)+1} mod 2^{\lfloor \frac{r}{k}\rfloor +1}) - (x_{(r mod k)+1} mod 2^{\lfloor \frac{r}{k}\rfloor})}{2^{\lfloor \frac{r}{k}\rfloor})}) 2^r$$
 | 
						||
 | 
						||
MDH Veranschaulichung
 | 
						||

 | 
						||
- verdeutlicht Komposition der Hashfunktion $h_i$ für drei Dimensionen und den Wert $i=7$
 | 
						||
- graphisch unterlegte Teile der Bitstrings entsprechen den Werten $h_{7_1}(x_1),h_{7_2}(x_2)$ und $h_{7_3}(x_3)$
 | 
						||
- beim Schritt auf $i=8$ würde ein weiteres Bit von $x_2$ (genauer: von $h_{8_2}(x_2)$) verwendet
 | 
						||
 | 
						||
MDH Komplexität
 | 
						||
- Exact-Match-Anfragen: $O(1)$
 | 
						||
- Partial-Match-Anfragen, bei $t$ von $k$ Attributen festgelegt, Aufwand $O(n^{1-\frac{t}{k}})$
 | 
						||
- ergibt sich aus der Zahl der Seiten, wenn bestimmte Bits "unknown"
 | 
						||
- Spezialfälle: $O(1)$ für $t=k$, $O(n)$ für $t=0$
 | 
						||
 | 
						||
## Geometrische Zugriffsstrukturen
 | 
						||
Geometrische Zugriffsstrukturen
 | 
						||
- große Mengen geometrischer Objekte ($> 10^6$)
 | 
						||
- Eigenschaften geometrischer Objekte
 | 
						||
    - Geometrie (etwa Polygonzug)
 | 
						||
    - zur Unterstützung bei Anfragen: zusätzlich $d$-dimensionales umschreibendes Rechteck (bounding
 | 
						||
       box)
 | 
						||
    - nichtgeometrische Attribute
 | 
						||
- Anwendungsszenarien: Geoinformationssysteme (Katasterdaten, Karten), CAx-Anwendungen (etwa VLSI
 | 
						||
    Entwurf), ...
 | 
						||
- Zugriff primär über Geometriedaten: Suchfenster (Bildschirmausschnitt), Zugriff auf benachbarte Objekte
 | 
						||
 | 
						||
Typische Operationen
 | 
						||
- exakte Suche
 | 
						||
    - Vorgabe: exakte geometrische Suchdaten
 | 
						||
    - Ergebnis: maximal ein Objekt
 | 
						||
- Bereichsanfrage für vorgegebenes $n$-dimensionales Fenster
 | 
						||
    - Suchfenster: $d$-dimensionales Rechteck (entspricht mehrdimensionalem Intervall)
 | 
						||
    - Ergebnis sind alle geometrischen Objekte, die das Suchfenster schneiden
 | 
						||
    - Ergebnisgröße parameterabhängig
 | 
						||
- Einfügen von geometrischen Objekten
 | 
						||
    - wünschenswert ohne globale Reorganisation!
 | 
						||
- Löschen von geometrischen Objekten
 | 
						||
    - wünschenswert ohne globale Reorganisation!
 | 
						||
 | 
						||
Nachbarschaftserhaltende Suchbäume
 | 
						||
- Aufteilung des geometrischen Bereichs in Regionen
 | 
						||
- benachbarte Objekte wenn möglich der selben Region zuordnen
 | 
						||
- falls dieses nicht geht, diese auf benachbarte Regionen aufteilen
 | 
						||
- Baumstruktur entsteht durch Verfeinerung von Regionen in benachbarte Teilregionen
 | 
						||
- Speicherung von Objekten erfolgt in den Blattregionen
 | 
						||
- Freiheitsgrade
 | 
						||
    - Form der Regionen
 | 
						||
    - vollständige Partitionierung oder Überlappung durch die Regionen
 | 
						||
    - eindeutige Zuordnung von Objekten zu Regionen oder Mehrfachzuordnung
 | 
						||
    - Speicherung und Zugriff über Originalgeometrie oder über abgeleitete Geometrie für Objekte
 | 
						||
    - Grad des Baumes & Organisationsform
 | 
						||
 | 
						||
Mehrstufige Bearbeitung geom. Anfragen
 | 
						||

 | 
						||
 | 
						||
Geom. Baumstruktur: BSP-Baum
 | 
						||
- Binary Space Partitioning: schrittweises binäres Teilen des Datenraums
 | 
						||

 | 
						||
 | 
						||
Realisierungsvarianten
 | 
						||
| Alternative | Baumstrukturen | | |
 | 
						||
| --- | --- | --- | --- |
 | 
						||
| | BSP-Baum | R-Baum | R+-Baum |
 | 
						||
Regionenform | konvexe Polygone |Rechtecke| Rechtecke|
 | 
						||
Teilregionen |vollständig |unvollständig |unvollständig|
 | 
						||
Überlappung |nein |ja |nein|
 | 
						||
ausgeglichen |nein |ja |ja|
 | 
						||
 | 
						||
R-Bäume
 | 
						||
- R-Baum: Verallgemeinerung des B-Baum-Prinzips auf mehrere Dimensionen
 | 
						||
    - Baumwurzel entspricht einem Rechteck, das alle geometrischen Objekte umfasst
 | 
						||
    - Geo-Objekte werden durch ihre umschließenden Rechtecke repräsentiert
 | 
						||
    - Aufteilung in Regionen erfolgt in nichtdisjunkte Rechtecke
 | 
						||
    - Jedes Geo-Objekt ist eindeutig einem Blatt zugeordnet
 | 
						||
- Regionenaufteilung durch Rechtecke im R-Baum
 | 
						||
    
 | 
						||
- Baumstruktur für R-Baum
 | 
						||
    
 | 
						||
 | 
						||
Probleme mit R-Bäumen
 | 
						||
- gegebenes Rechteck kann von vielen Regionen überlappt werden, es ist aber genau in einer Region gespeichert
 | 
						||
- auch Punktanfragen können eine Suche in sehr vielen Rechteckregionen bedeuten
 | 
						||
- Ineffizient bei exakter Suche (exakte Suche auch bei Einfügen und Löschen notwendig!)
 | 
						||
- Probleme beim Einfügen
 | 
						||
    - Einfügen erfordert oft Vergrößern von Regionen (aufwärts propagiert)
 | 
						||
 | 
						||
Vergrößern beim Einfügen
 | 
						||

 | 
						||
 | 
						||
R+-Bäume
 | 
						||
- R+-Bäume: Aufteilung in Teilregionen disjunkt
 | 
						||
- Jedem gespeicherten Punkt des geometrischen Bereichs ist eindeutig ein Blatt zugeordnet
 | 
						||
- In jeder Baumebene ist einem Punkt ebenfalls maximal ein Rechteck zugeordnet -> eindeutiger Pfad von der Wurzel zum speichernden Blatt
 | 
						||
- ‘Clipping’ von Geo-Objekten notwendig!
 | 
						||
    
 | 
						||
    
 | 
						||
 | 
						||
Probleme mit R+-Bäumen
 | 
						||
- Objekte müssen in mehreren Rechteckregionen gespeichert werden (clipping) - erhöhter Speicher- & Modifikationsaufwand
 | 
						||
- Einfügen von Objekten erfordert möglicherweise Modifikation mehrerer Rechteckregionen
 | 
						||
    
 | 
						||
- Einfügen kann in bestimmten Situationen unvermeidbar zu Regionenaufteilungen führen
 | 
						||
    
 | 
						||
- Regionenmodifikationen haben Konsequenzen sowohl in Richtung Blätter als auch in Richtung Wurzel
 | 
						||
- obere Grenze für Einträge in Blattknoten kann nicht mehr garantiert werden
 | 
						||
 | 
						||
Rechteckspeicherung durch Punktdatenstrukturen
 | 
						||
- Probleme bei ausgedehnten Geometrien -> Entartung
 | 
						||
- Nutzung "robuster" mehrdimensionaler Indexstrukturen (z.B. Grid-File) durch Abbildung von Rechtecken auf Punkte möglich?
 | 
						||
 | 
						||
Punktdatenstrukturen
 | 
						||
- Rechteckspeicherung durch Punktdatenstrukturen
 | 
						||
    - Transformation von ausgedehnten Objekten (mehrdimensionale Rechtecke) in Punktdaten
 | 
						||
    - Transformation bildet $d$-dimensionale Rechtecke auf Punkte im $2d$-dimensionalen Raum $R^{2d}$ ab
 | 
						||
    - $d$-dimensionale Rechteck: $r=[l_1, r_1]\times ...\times [l_d, r_d]$
 | 
						||
- Eckentransformation
 | 
						||
    $p_r = (l_1, r_1,..., l_d, r_d) \in R^{2d}$
 | 
						||
    pro Intervall als Koordinaten: obere Schranke, untere Schranke
 | 
						||
- Mittentransformation $p_r =(\frac{l_1+r_1}{2}, \frac{r_1-l_1}{2},...,\frac{l_d+l_r}{2},\frac{r_d-l_d}{2})\in\mathbb{R}^{2d}$
 | 
						||
    pro Intervall als Koordinaten: Mittelpunkt, halbe Breite
 | 
						||
 | 
						||
Eckentransformation
 | 
						||

 | 
						||
 | 
						||
Mittentransformation
 | 
						||

 | 
						||
 | 
						||
Suchfenster
 | 
						||

 | 
						||
 | 
						||
Grid-File-Degeneration
 | 
						||

 | 
						||
 | 
						||
## Approximierende Verfahren
 | 
						||
Approximierende Verfahren
 | 
						||
- insbesondere für hochdimensionale Daten
 | 
						||
- pro Tupel einen approximierenden Bit-Code mit nur wenigen Bits pro Dimensionen
 | 
						||
       - beispielsweise ein Grid mit jeweils $2^k$ Bit pro Dimension
 | 
						||
       - ordnungserhaltend wenn die Bits geeignet gewählt werden
 | 
						||
       - adressiert werden die Zellen in denen ein Punkt liegt
 | 
						||
       - Nutzung zur Nachbarschaftssuche in hochdimensionalen Räumen
 | 
						||
- VA-File von Weber et al.
 | 
						||
    - ersetzt Durchlauf über alle Punkte durch Durchlauf über alle Approximationswerte zur Vorauswahl von Kandidaten
 | 
						||
 | 
						||
VA-File
 | 
						||

 | 
						||
 | 
						||
Zusammenfassung
 | 
						||
- Bitmap-Indexe
 | 
						||
- Digital- und Präfixbäume
 | 
						||
- Mehrdimensionale Verfahren
 | 
						||
- Geometrische Zugriffsstrukturen
 |