0
# Multi-Database Support
1
2
LMDB environments can contain multiple named databases for data organization and isolation. This enables logical separation of different data types while maintaining ACID transaction guarantees across all databases within a single environment.
3
4
## Capabilities
5
6
### Database Configuration
7
8
Configure environment to support multiple databases and create named databases with specialized options.
9
10
```python { .api }
11
def open(path: str, max_dbs: int = 0, **kwargs) -> Environment:
12
"""
13
Open environment with multi-database support.
14
15
Parameters:
16
- path: Environment path
17
- max_dbs: Maximum number of named databases (0 = default database only)
18
- **kwargs: Other environment options
19
20
Returns:
21
Environment instance
22
23
Note:
24
max_dbs must be set > 0 to use named databases
25
"""
26
27
class Environment:
28
def open_db(self, key: bytes = None, txn=None, reverse_key: bool = False,
29
dupsort: bool = False, create: bool = True, integerkey: bool = False,
30
integerdup: bool = False, dupfixed: bool = False) -> _Database:
31
"""
32
Open or create named database within environment.
33
34
Parameters:
35
- key: Database name (None for default database)
36
- txn: Transaction to use (temporary read-write transaction created if None)
37
- reverse_key: Keys stored in reverse byte order
38
- dupsort: Allow and sort duplicate keys
39
- create: Create database if it doesn't exist
40
- integerkey: Keys are native byte-order integers
41
- integerdup: Duplicate values are native integers (requires dupsort)
42
- dupfixed: All duplicate values same size (requires dupsort)
43
44
Returns:
45
Database handle for use in transactions
46
47
Raises:
48
Error if max_dbs=0 and key is not None
49
"""
50
```
51
52
### Database Handle Management
53
54
Work with database handles across transactions and manage database metadata.
55
56
```python { .api }
57
class _Database:
58
"""
59
Database handle representing a named database within an LMDB environment.
60
61
Database handles are returned by Environment.open_db() and used to specify
62
which database to operate on in transactions and cursors. Database handles
63
are lightweight objects that can be safely passed between transactions.
64
65
Note:
66
Database handles remain valid until the environment is closed.
67
"""
68
69
def flags(self, *args) -> dict:
70
"""
71
Get database configuration flags.
72
73
Returns:
74
Dictionary containing database flags:
75
- reverse_key: Keys stored in reverse order
76
- dupsort: Duplicate keys allowed and sorted
77
- integerkey: Keys are native integers
78
- integerdup: Duplicate values are native integers
79
- dupfixed: All duplicate values are same size
80
"""
81
```
82
83
### Cross-Database Transactions
84
85
Perform atomic operations across multiple databases within single transactions.
86
87
```python { .api }
88
class Transaction:
89
def get(self, key: bytes, default=None, db=None) -> bytes:
90
"""
91
Get value from specific database.
92
93
Parameters:
94
- key: Key to retrieve
95
- default: Default value if key not found
96
- db: Database handle (uses transaction default if None)
97
"""
98
99
def put(self, key: bytes, value: bytes, db=None, **kwargs) -> bool:
100
"""
101
Store key-value pair in specific database.
102
103
Parameters:
104
- key: Key bytes
105
- value: Value bytes
106
- db: Database handle (uses transaction default if None)
107
- **kwargs: Additional put options
108
"""
109
110
def delete(self, key: bytes, value: bytes = b'', db=None) -> bool:
111
"""
112
Delete key from specific database.
113
114
Parameters:
115
- key: Key to delete
116
- value: Specific value for duplicate keys
117
- db: Database handle (uses transaction default if None)
118
"""
119
120
def cursor(self, db=None) -> Cursor:
121
"""
122
Create cursor for specific database.
123
124
Parameters:
125
- db: Database handle (uses transaction default if None)
126
"""
127
128
def stat(self, db) -> dict:
129
"""
130
Get statistics for specific database.
131
132
Parameters:
133
- db: Database handle
134
135
Returns:
136
Database statistics dictionary
137
"""
138
139
def drop(self, db, delete: bool = True) -> None:
140
"""
141
Empty or delete specific database.
142
143
Parameters:
144
- db: Database handle
145
- delete: If True delete database, if False just empty it
146
147
Note:
148
Cannot delete default database (key=None), only empty it
149
"""
150
```
151
152
### Usage Examples
153
154
#### Basic Multi-Database Setup
155
156
```python
157
import lmdb
158
159
# Open environment with support for multiple databases
160
env = lmdb.open('/path/to/database', max_dbs=10)
161
162
# Create different databases for different data types
163
users_db = env.open_db(b'users')
164
products_db = env.open_db(b'products')
165
orders_db = env.open_db(b'orders')
166
settings_db = env.open_db(b'settings')
167
168
# Use default database (no name)
169
default_db = env.open_db() # or env.open_db(None)
170
171
print("Databases created successfully")
172
env.close()
173
```
174
175
#### Atomic Cross-Database Operations
176
177
```python
178
import lmdb
179
import json
180
181
env = lmdb.open('/path/to/database', max_dbs=5)
182
183
users_db = env.open_db(b'users')
184
accounts_db = env.open_db(b'accounts')
185
transactions_db = env.open_db(b'transactions')
186
187
# Atomic money transfer across multiple databases
188
def transfer_money(from_user: str, to_user: str, amount: float, tx_id: str):
189
with env.begin(write=True) as txn:
190
# Get current balances
191
from_account = json.loads(txn.get(from_user.encode(), db=accounts_db) or b'{"balance": 0}')
192
to_account = json.loads(txn.get(to_user.encode(), db=accounts_db) or b'{"balance": 0}')
193
194
# Check sufficient funds
195
if from_account['balance'] < amount:
196
raise ValueError("Insufficient funds")
197
198
# Update balances
199
from_account['balance'] -= amount
200
to_account['balance'] += amount
201
202
# Store updated balances
203
txn.put(from_user.encode(), json.dumps(from_account).encode(), db=accounts_db)
204
txn.put(to_user.encode(), json.dumps(to_account).encode(), db=accounts_db)
205
206
# Log transaction
207
tx_record = {
208
'id': tx_id,
209
'from': from_user,
210
'to': to_user,
211
'amount': amount,
212
'timestamp': time.time()
213
}
214
txn.put(tx_id.encode(), json.dumps(tx_record).encode(), db=transactions_db)
215
216
print(f"Transfer completed: {from_user} -> {to_user}, ${amount}")
217
218
# Create users and initial accounts
219
with env.begin(write=True) as txn:
220
txn.put(b'alice', b'{"name": "Alice", "email": "alice@example.com"}', db=users_db)
221
txn.put(b'bob', b'{"name": "Bob", "email": "bob@example.com"}', db=users_db)
222
223
txn.put(b'alice', b'{"balance": 1000.0}', db=accounts_db)
224
txn.put(b'bob', b'{"balance": 500.0}', db=accounts_db)
225
226
# Perform transfer
227
transfer_money('alice', 'bob', 100.0, 'tx001')
228
229
env.close()
230
```
231
232
#### Database-Specific Configuration
233
234
```python
235
import lmdb
236
237
env = lmdb.open('/path/to/database', max_dbs=5)
238
239
# Create databases with different configurations
240
users_db = env.open_db(b'users') # Standard key-value
241
tags_db = env.open_db(b'tags', dupsort=True) # Allow duplicate keys
242
counters_db = env.open_db(b'counters', integerkey=True) # Integer keys
243
reverse_db = env.open_db(b'reverse', reverse_key=True) # Reverse key order
244
245
# Store data using different database configurations
246
with env.begin(write=True) as txn:
247
# Standard database
248
txn.put(b'user1', b'Alice', db=users_db)
249
txn.put(b'user2', b'Bob', db=users_db)
250
251
# Database with duplicate keys (tags for posts)
252
txn.put(b'post1', b'python', db=tags_db)
253
txn.put(b'post1', b'tutorial', db=tags_db) # Same key, different value
254
txn.put(b'post1', b'beginner', db=tags_db)
255
256
# Integer key database
257
import struct
258
key1 = struct.pack('i', 1)
259
key2 = struct.pack('i', 100)
260
key3 = struct.pack('i', 50)
261
txn.put(key1, b'counter_one', db=counters_db)
262
txn.put(key2, b'counter_hundred', db=counters_db)
263
txn.put(key3, b'counter_fifty', db=counters_db)
264
265
# Reverse key order database
266
txn.put(b'zebra', b'last_animal', db=reverse_db)
267
txn.put(b'apple', b'first_fruit', db=reverse_db)
268
txn.put(b'banana', b'second_fruit', db=reverse_db)
269
270
# Read and verify data
271
with env.begin() as txn:
272
print("Standard database:")
273
for key, value in txn.cursor(db=users_db):
274
print(f" {key} = {value}")
275
276
print("\\nDuplicate keys database:")
277
for key, value in txn.cursor(db=tags_db):
278
print(f" {key} = {value}")
279
280
print("\\nInteger keys database (sorted by integer value):")
281
for key, value in txn.cursor(db=counters_db):
282
int_key = struct.unpack('i', key)[0]
283
print(f" {int_key} = {value}")
284
285
print("\\nReverse order database:")
286
for key, value in txn.cursor(db=reverse_db):
287
print(f" {key} = {value}")
288
289
env.close()
290
```
291
292
#### Database Statistics and Management
293
294
```python
295
import lmdb
296
297
env = lmdb.open('/path/to/database', max_dbs=3)
298
299
users_db = env.open_db(b'users')
300
posts_db = env.open_db(b'posts')
301
comments_db = env.open_db(b'comments')
302
303
# Add sample data
304
with env.begin(write=True) as txn:
305
# Users
306
for i in range(100):
307
txn.put(f'user{i}'.encode(), f'User {i}'.encode(), db=users_db)
308
309
# Posts
310
for i in range(50):
311
txn.put(f'post{i}'.encode(), f'Post {i} content'.encode(), db=posts_db)
312
313
# Comments
314
for i in range(200):
315
post_id = i % 50
316
txn.put(f'comment{i}'.encode(), f'Comment on post{post_id}'.encode(), db=comments_db)
317
318
# Get statistics for each database
319
with env.begin() as txn:
320
databases = [
321
('users', users_db),
322
('posts', posts_db),
323
('comments', comments_db)
324
]
325
326
for name, db in databases:
327
stats = txn.stat(db)
328
print(f"\\n{name.capitalize()} database statistics:")
329
print(f" Entries: {stats['entries']}")
330
print(f" Leaf pages: {stats['leaf_pages']}")
331
print(f" Branch pages: {stats['branch_pages']}")
332
print(f" Overflow pages: {stats['overflow_pages']}")
333
print(f" Tree depth: {stats['depth']}")
334
335
# Empty a database
336
with env.begin(write=True) as txn:
337
print(f"\\nComments before drop: {txn.stat(comments_db)['entries']}")
338
txn.drop(comments_db, delete=False) # Empty but don't delete
339
print(f"Comments after drop: {txn.stat(comments_db)['entries']}")
340
341
env.close()
342
```
343
344
#### Database Isolation Example
345
346
```python
347
import lmdb
348
import threading
349
import time
350
351
env = lmdb.open('/path/to/database', max_dbs=2)
352
353
db1 = env.open_db(b'database1')
354
db2 = env.open_db(b'database2')
355
356
def worker(worker_id: int, database):
357
"""Worker function that operates on specific database"""
358
with env.begin(write=True) as txn:
359
for i in range(10):
360
key = f'worker{worker_id}_item{i}'.encode()
361
value = f'data from worker {worker_id}'.encode()
362
txn.put(key, value, db=database)
363
time.sleep(0.1) # Simulate work
364
365
print(f"Worker {worker_id} completed")
366
367
# Create threads working on different databases
368
thread1 = threading.Thread(target=worker, args=(1, db1))
369
thread2 = threading.Thread(target=worker, args=(2, db2))
370
371
# Start concurrent operations on different databases
372
thread1.start()
373
thread2.start()
374
375
thread1.join()
376
thread2.join()
377
378
# Verify data isolation
379
with env.begin() as txn:
380
print("Database 1 contents:")
381
for key, value in txn.cursor(db=db1):
382
print(f" {key} = {value}")
383
384
print("\\nDatabase 2 contents:")
385
for key, value in txn.cursor(db=db2):
386
print(f" {key} = {value}")
387
388
env.close()
389
```