Ce diaporama a bien été signalé.
Le téléchargement de votre SlideShare est en cours. ×

Implementing a key/value store

Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Prochain SlideShare
Redis - (nosqlfr meetup #2)
Redis - (nosqlfr meetup #2)
Chargement dans…3
×

Consultez-les par la suite

1 sur 34 Publicité

Plus De Contenu Connexe

Diaporamas pour vous (20)

Similaire à Implementing a key/value store (20)

Publicité

Plus récents (20)

Implementing a key/value store

  1. 1. 50 AVENUE DES CHAMPS-ÉLYSÉES 75008 PARIS > FRANCE > WWW.OCTO.COM Implementing a Key / Value Store BluckDB BJC - BOF - 15/12/16 github.com/BenJoyenConseil/bluckdb @BenJoyeConseil
  2. 2. Pourquoi ... ◉ Comprendre les mécanismes des bases modernes ◉ Explorer les algos et les structures de donnée ◉ Faire du “bas niveau” ◉ Apprendre Go ◉ Ne pas être à poils lors de la formation HBase Réinventer la roue ?
  3. 3. >01 Situer le kv store
  4. 4. Souvent présenté comme ça Storage engine !
  5. 5. ◉ LevelDB (LSM-Tree) ◉ RocksDB (LSM-Tree) ◉ WiredTiger (LSM-Tree) ◉ ForestDB (HB+Trie) ◉ InnoDB (B+Tree) ◉ BoltDB (B+Tree) ◉ Kyoto Cabinet (Hashtable) ◉ BluckDB (Hashtable) ◉ ... database Exemples de KV store comme moteur de stockage LevelDB ... InnoDB Server MongoDB / MySQL / Riak / Lucene / ... File system
  6. 6. Cockroachdb utilise RocksDB comme moteur de stockage
  7. 7. Quotable quote All models are wrong but some are useful — George Box Models
  8. 8. >02 Deep Dive
  9. 9. Le design 1. Data storage abstraction 2. Data structure (index) 3. Memory management (page / block management, free space) 4. String / byte slice 5. Iterator / Cursor 6. Lock management 7. Comparator Top 7 des composants dans un kv store (ref : article topito)
  10. 10. L’interface type KVStore interface { Get(k string) string Put(k, v string) Delete(k string) }
  11. 11. First implem’ ◉ Simple hashmap (separate chaining) ◉ Persistent store : > Put -> append to file > Get -> foreach line, split(‘:’) MVP bucket files (fixed number) hash(k) % numBucket (static hashing) File bucket 1 File bucket 2 File bucket 3 append hash(k) % 3
  12. 12. First implem’ ◉ Benchmarks persistent store BenchmarkPutNaiveDiskKVStore-4 200000 6250 ns/op -> 6,2 µs BenchmarkGetNaiveDiskKVStore-4 30 44017416 ns/op -> 44 ms ◉ Benchmarks in-memory hashmap BenchmarkPutHashMap-4 1000000 1385 ns/op -> 1,3 µs BenchmarkGetHashMap-4 2000000 711 ns/op -> 0,7 µs MVP
  13. 13. Quotable quote There’s clearly a trade-off between reads and writes, and it’s the mixing of the two that causes all of the interesting challenges — Adrian Colyer
  14. 14. >03 Ok, on fait un vrai design maintenant ? Hashtables are arguably the single most important data structure known to mankind. — Steve Yegge
  15. 15. Le design 1. Data storage abstraction -> SSD Page 4k + Record 2. Data structure (index) -> Hashtable (extendible hash) 3. Memory management (page / block management, free space) -> mmap + custom 4. String / byte slice -> Go string native conv to []byte slice 5. Iterator / Cursor -> Pattern iterator 6. Lock management -> à l’extérieur 7. Comparator -> multi-level comparator (key length > hash > byte) Top 7 des composants dans un kv store (ref : article topito)
  16. 16. Record layout type Record interface { key() []byte val() []byte valLen() uint16 // min 0 keyLen() uint16 // max 65536 } type ByteRecord []byte r := ByteRecord(byteArray[204 : 249]) overhead : 4 bytes
  17. 17. Record layout func (r ByteRecord) Write(key, val string) { ... copy( r[ : ], key) copy( r[ lenKey : ], val ) binary.LittleEndian.PutUint16( r[ total : ], lenVal ) binary.LittleEndian.PutUint16( r[ total + RECORD_HEADER_SIZE : ], lenKey ) } serialization ... k e y v a l u e 0x5 0x0 0x3 0x0 ...
  18. 18. Page layout type Page []byte const ( PAGE_SIZE = 4096 PAGE_USE_OFFSET = 4094 PAGE_LOCAL_DEPTH_OFFSET = 4092 ) func (p Page) Use() int { return int( binary.LittleEndian.Uint16( p[PAGE_USE_OFFSET : ] ) ) } ... Record1 Record2 Record3 Record1 v2 LD USE
  19. 19. Extendible Hashing algorithm dynamic hashing ◉ Une fonction de Hash qui génère des résultats sur un large segment — typiquement un int32 ◉ Préfixe du résultat de la fonction de Hash pour calculer l’indice dans la table d’adresse. ◉ Plusieurs entrées dans la table d’adresse peuvent pointer sur la même page
  20. 20. Extendible Hashing algorithm p0 p0 p1 p2 ..00 ..01 ..10 ..11 ...0100 key, value ...1101 key, value ld=1 GD=2 ...0110 key, value ...1110 key, value ...0111 key, value ...1011 key, value ld=2 ld=2 page 1 page 2 page 3 Page addresses
  21. 21. Extendible Hashing algorithm Après le split p0 p4 p1 p2 ..00 ..01 ..10 ..11 ...0100 key, value ld=2 GD=2 ...0110 key, value ...1110 key, value ...0111 key, value ...1011 key, value ld=2 ld=2 page 1 page 2 page 3 Page addresses ...1101 key, value ld=2 page 4
  22. 22. Extendible Hashing algorithm Après le expand p0 p4 p1 p2 p0 p4 p1 p2 .000 .001 .010 .011 .100 .101 .110 .111 ...0100 key, value ld=2 GD=3 ...0110 key, value ...1110 key, value ...0111 key, value ...1011 key, value ld=2 ld=2 page 1 page 2 page 3 ...1101 key, value ld=2 page 4
  23. 23. Directory layout func (dir *Directory) extendibleHash(k util.Hashable) int { return k.Hash() & ((1 << dir.Gd) - 1) } func (dir *Directory) getPage(k string) (Page) { hash := dir.extendibleHash(util.Key(k)) id := dir.Table[hash] offset := id * PAGE_SIZE return Page(dir.data[offset : offset + PAGE_SIZE]) } dir := &Directory{ Table: []int{0, 1, 3, 2, 0, 1, 3, 2}, Gd: 2, LastPageId: 3, data: []byte{...}, } Mmap
  24. 24. Core Data Storage Memory Management Design Bluck server http.Listen Bluckstore open() close() Directory Mmap persistMeta() KVStore Get(k) Put(k,v) Delete(k) Page GC() ld() Lock Management RWMutex File Record Write() Iterator
  25. 25. >04 On parle des perf ? Memory maps are the best thing known to mankind after hash tables. — Emmanuel Goossaert
  26. 26. Memory mapping ◉ Mmap file -> []byte > 0 copy, pas de passage de user space à kernel space > Pas de buffer à gérer pour le flush > Pas de block cache ◉ Pré-allocation des pages pour accélérer mmap Optims
  27. 27. Chronologie des évolutions Les benchmarks (Go) Get Put Mmap 2344 5452 Update 2881 10532 Iterator 8796 11102 ByteRecord 1819 9982 GOB serde 1874 3206 Reverse 1406 1529 Flush Meta 1398 2786 Pre allocation 1408 1359
  28. 28. Les fonctionnalités qui font tout PÉTER !!! ◉ Update (in place ?) ◉ Metadata (consistency) ◉ Delete (shift? scan ?) ◉ Concurrency & Isolation ◉ Big record
  29. 29. $ go tool pprof Avant : Benchmark => itérations : 200.000 8796 ns/op 3376 B/op 106 allocs/op (pprof) top10 1590ms of 1890ms total (84.13%) Showing top 10 nodes out of 77 (cum >= 50ms) flat flat% sum% cum cum% 500ms 26.46% 26.46% 920ms 48.68% runtime.mallocgc 260ms 13.76% 40.21% 260ms 13.76% runtime.heapBitsSetType 140ms 7.41% 47.62% 1630ms 86.24% github.com/BenJoyenConseil/bluckdb/bluckstore/mmap.Page.get 140ms 7.41% 55.03% 1430ms 75.66% runtime.convT2I 120ms 6.35% 61.38% 120ms 6.35% runtime.memclr 120ms 6.35% 67.72% 120ms 6.35% runtime.memmove cpu
  30. 30. $ go tool pprof Après : Benchmark => itérations : 2.000.000 756 ns/op 16 B/op 1 allocs/op (pprof) top10 2.33s of 2.36s total (98.73%) Dropped 9 nodes (cum <= 0.01s) Showing top 10 nodes out of 12 (cum >= 2.36s) flat flat% sum% cum cum% 2.21s 93.64% 93.64% 2.33s 98.73% github.com/BenJoyenConseil/bluckdb/bluckstore/mmap.Page.get 0.06s 2.54% 96.19% 0.11s 4.66% runtime.mallocgc 0.03s 1.27% 97.46% 0.03s 1.27% runtime.heapBitsSetType 0.02s 0.85% 98.31% 0.02s 0.85% runtime.scanobject 0.01s 0.42% 98.73% 0.12s 5.08% runtime.newobject cpu
  31. 31. Recap des compromis ◉ Mmap avec pré-allocation des pages : supprimer des enregistrements ne libère pas l’espace disque ◉ Extendible Hashing & hashtable : performance pour l’accès, mais la persistance des meta appart est très coûteuse et risquée (d’un point de vue cohérence) ◉ Delete : marquer les kv comme “deleted” augmente fortement le phénomène Write Amplification versus shifting déplace beaucoup de données ◉ Update : “in place” il faut gérer la défragmentation versus append only il faut faire du GC ◉ Concurrence : faire un hashtable threadsafe complexifie beaucoup le code (Mutex)
  32. 32. Recap des compromis ◉ La structure de donnée (Hashtable, LSM-Tree, B+Tree, Trie, etc...) définit le(s) compromis > Latence lecture > Latence écriture > Range scan > Degré d’isolation > Cohérence > Haute dispo ◉ Choisissez !!
  33. 33. BluckDB github.com/BenJoyenConseil/bluckdb @BenJoyeConseil Fork it

×