patx/pickledb
pickleDB is an in memory key-value store using Python's orjson module for persistence. It can also use SQLite instead of orjson if needed.
$ git clone https://gitman.io/git/patx/pickledb
pickleDB: Your Lightweight, High-Speed Key-Value Store
pickleDB is a lightweight, in-memory key-value store designed for developers who want simplicity, speed, and reliability — without sacrificing modern capabilities. BSD 3-Clause License © Harrison Erd.
- 💫 Blazing Speed: Backed by the high-performance orjson library, pickleDB handles millions of records with ease. Perfect for applications where every millisecond counts
- 😋 Ridiculously Easy to Use: With its minimalist API, pickleDB makes adding, retrieving, and managing your data as simple as writing a Python list. No steep learning curves. No unnecessary complexity.
- 🔒 Rock-Solid Reliability: Your data deserves to be safe. Atomic saves ensure your database remains consistent—even if something goes wrong.
- 🐍 Simple Pythonic Flexibility: Store strings, lists, dictionaries, and more—all with native Python operations. No need to learn special commands. If you know Python, you already know pickleDB.
- 🙋 Community & Contributions: We’re passionate about making pickleDB better every day. Got ideas, feedback, or an issue to report? Let’s connect on GitHub Issues
- 💾 Portable: Data is stored as standard JSON, human-readable and cross-language friendly.
- 🕸️ Async-Ready: Non-blocking I/O with aiofiles. Works with web frameworks like Starlette, FastAPI, or MicroPie.
- ⚡ Unified API: One class, one set of methods - works seamlessly in both sync and async environments.
- 💢 Limitations: The entire dataset resides in memory while loaded which might be a constraint on systems with limited RAM for extremely large datasets. pickleDB is designed for simplicity, so it may not meet the needs of applications requiring advanced database features. For larger-scale or concurrent applications requiring a more robust, consider DataSet, Redis, SQLite, or MongoDB.
- 📎 Useful Links: GitHub - PyPI - Report an Issue/Ask for Help - Documentation
Getting Started
Installation
Install via pip:
pip install pickledb
Synchronous Example
from pickledb import PickleDB
db = PickleDB("data.json")
db.load()
db.set("username", "alice")
db.set("theme", {"color": "blue", "font": "sans-serif"})
print(db.get("username")) # → "alice"
db.save()
Asynchronous Example
import asyncio
from pickledb import PickleDB
async def main():
async with PickleDB("data.json") as db:
await db.set("score", 42)
value = await db.get("score")
print(value) # → 42
asyncio.run(main())
Core Methods
| Method | Description |
|---|---|
load() |
Loads the database from disk (async-aware). |
save() |
Atomically saves the database to disk. |
set(key, value) |
Sets or updates a key. |
get(key, default=None) |
Returns the value for a key. |
remove(key) |
Deletes a key if it exists. |
all() |
Returns a list of all keys. |
purge() |
Clears the entire database. |
All of these methods can be used synchronously or asynchronously — just await them if inside an event loop.
Performance Highlights
pickleDB demonstrates strong performance for handling large-sized datasets:
| Entries | Memory Load Time | Retrieval Time | Save Time |
|---|---|---|---|
| 1M | 0.68 sec | 0.64 sec | 0.03 sec |
| 10M | 7.48 sec | 7.27 sec | 0.22 sec |
| 50M | 43.36 sec | 36.53 sec | 1.09 sec |
Tests were performed on a Dell XPS 9350 running Ubuntu 24.04 using pickleDB's async mode.
User Guide and Examples
Add or Update Data
You can add or update key-value pairs using the set() method:
# Add a new key-value pair
db.set('username', 'admin')
# Or shorthand
db['username'] = 'admin'
# Update an existing key-value pair
db.set('username', 'superadmin')
print(db.get('username')) # Output: 'superadmin'
Keys are automatically converted to strings, and values can be any JSON-serializable object.
Retrieve Values
You can retrieve a keys value using the get() method:
# Get the value for a key
print(db.get('username')) # Output: 'superadmin'
# Using Python syntax sugar
db['username'] # Output: 'superadmin'
# Attempt to retrieve a non-existent key
print(db.get('nonexistent')) # Output: None
List All Keys
You can get a list of all the keys currently in the database using the all() method:
db.set('item1', 'value1')
db.set('item2', 'value2')
print(db.all()) # Output: ['username', 'item1', 'item2']
Note: This method shows all keys currently loaded, it does not guarantee they are persisted to the disk (yet).
Remove Keys
To remove a key from the database use the remove() method:
db.remove('item1')
print(db.all()) # Output: ['username', 'item2']
Purge the Database
To remove all keys and their values from the database use the purge() method:
db.purge()
print(db.all()) # Output: []
Saving Data
pickleDB does not auto-save by default for performance reasons. To persist data, call save() manually or use a context manager:
db.save() # Output: True
# Context manager example
with db:
db.set('foo', 'bar')
db.set('hello', 'world')
# Automatically saves when exiting the context
Note: All the above methods work/display on the in-memory database. To persist any of the above methods actions use must call the save() method or use a context manager, as stated above.
Asynchronous Usage
pickleDB 1.4 uses a single unified class for both synchronous and asynchronous contexts.
import asyncio
from pickledb import PickleDB
async def main():
async with PickleDB('data.json') as db:
await db.set('score', 42)
print(await db.get('score')) # Output: 42
asyncio.run(main())
Just await any method when inside an async function/event-loop.
Store and Retrieve Complex Data
# Store a dictionary
db.set('user', {'name': 'Alice', 'age': 30, 'city': 'Wonderland'})
# Retrieve and modify
user = db.get('user')
user['age'] += 1
db.set('user', user)
print(db.get('user'))
# Output: {'name': 'Alice', 'age': 31, 'city': 'Wonderland'}
Use Lists for Dynamic Data
db.set('tasks', ['Write code', 'Test app', 'Deploy'])
tasks = db.get('tasks')
tasks.append('Celebrate')
db.set('tasks', tasks)
print(db.get('tasks'))
# Output: ['Write code', 'Test app', 'Deploy', 'Celebrate']
Advanced Key Search
You can filter keys dynamically using Python list comprehensions:
def get_keys_with_match(db_instance, match):
return [key for key in db_instance.all() if match in key]
db.set('apple', 1)
db.set('apricot', 2)
db.set('banana', 3)
print(get_keys_with_match(db, 'ap'))
# Output: ['apple', 'apricot']
Namespaces
Simulate namespaces using prefixes:
db.set('user:1', {'name': 'Alice'})
db.set('user:2', {'name': 'Bob'})
def get_namespace_keys(db_instance, namespace):
return [key for key in db_instance.all() if key.startswith(f"{namespace}:")]
print(get_namespace_keys(db, 'user'))
# Output: ['user:1', 'user:2']
Key Expiration (TTL)
pickleDB doesn’t include TTL natively, but you can simulate it:
import time
def set_with_expiry(db, key, value, ttl):
db.set(key, {'value': value, 'expires_at': time.time() + ttl})
def get_if_not_expired(db, key):
data = db.get(key)
if data and time.time() < data['expires_at']:
return data['value']
db.remove(key)
return None
set_with_expiry(db, 'session', 'active', ttl=5)
time.sleep(3)
print(get_if_not_expired(db, 'session')) # 'active'
time.sleep(3)
print(get_if_not_expired(db, 'session')) # None
Encrypted Storage
from cryptography.fernet import Fernet
key = Fernet.generate_key()
cipher = Fernet(key)
encrypted = cipher.encrypt(b"My secret data")
db.set('secure', encrypted)
decrypted = cipher.decrypt(db.get('secure'))
print(decrypted.decode()) # Output: My secret data
Batch Operations
def batch_set(db, items):
for key, value in items.items():
db.set(key, value)
batch_set(db, {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'})
print(db.all())
def batch_delete(db, keys):
for key in keys:
db.remove(key)
batch_delete(db, ['k1', 'k2'])
print(db.all())
Key Pattern Matching
import re
def get_keys_by_pattern(db, pattern):
regex = re.compile(pattern)
return [key for key in db.all() if regex.search(key)]
db.set('user:1', {'name': 'Alice'})
db.set('user:2', {'name': 'Bob'})
db.set('admin:1', {'name': 'Charlie'})
print(get_keys_by_pattern(db, r'user:\d'))
# Output: ['user:1', 'user:2']
Signal Handling for Graceful Shutdowns
import signal, sys
from pickledb import PickleDB
db = PickleDB('data.json')
signal.signal(signal.SIGINT, lambda s, f: (db.save(), sys.exit(0)))
signal.signal(signal.SIGTERM, lambda s, f: (db.save(), sys.exit(0)))
db.set('key1', 'value1')
print("Running... Press Ctrl+C to save and exit.")
while True:
pass
Using pickleDB with Web Frameworks
Example using MicroPie:
from uuid import uuid4
from micropie import App
from pickledb import PickleDB
db = PickleDB('pastes.json')
class Root(App):
async def index(self, paste_content=None):
if self.request.method == "POST":
pid = str(uuid4())
await db.set(pid, paste)
await db.save()
return self._redirect(f'/paste/{pid}')
return await self._render_template('index.html')
async def paste(self, paste_id):
paste = await db.get(paste_id)
return await self._render_template('paste.html', paste_id=paste_id, paste_content=paste)
app = Root()
Core API Reference
Class: PickleDB
class PickleDB(location: str)
A lightweight, JSON-backed key-value database. All data is kept in memory while loaded and written atomically to disk on save().
Parameters
| Name | Type | Description |
|---|---|---|
location |
str |
Path to the JSON file backing the database. Tilde (~) is expanded. |
Context Manager Support
Synchronous
with PickleDB("data.json") as db:
db.set("foo", "bar")
Asynchronous
async with PickleDB("data.json") as db:
await db.set("foo", "bar")
On successful exit, the DB is automatically saved.
Method Reference
load()
load() -> None
await load() -> None
Loads the database into memory from disk if the file exists and contains valid JSON. Creates an empty database otherwise.
save()
save() -> bool
await save() -> bool
Writes the in-memory database to disk.
Returns True on success.
Notes:
- Uses a temporary file + os.replace for durability.
- Automatically called on successful context-manager exit.
set()
set(key: str, value: Any) -> bool
await set(key: str, value: Any) -> bool
Sets or updates a value for a key.
key is coerced to str, and value must be JSON-serializable.
Returns True.
Syntax Sugar
db["username"] = "alice"
get()
get(key: str, default: Any = None) -> Any
await get(key: str, default: Any = None) -> Any
Retrieves a value by key.
Returns:
- stored value
- default if key does not exist
Syntax Sugar
value = db["username"]
remove()
remove(key: str) -> bool
await remove(key: str) -> bool
Removes a key.
Returns:
- True if removed
- False if not found
all()
all() -> list[str]
await all() -> list[str]
Returns a list of all keys currently in memory.
purge()
purge() -> bool
await purge() -> bool
Clears the entire in-memory database.
Returns:
- True
Synchronous vs Asynchronous Behavior
All public methods of PickleDB work in both runtimes:
| Environment | Usage |
|---|---|
| Synchronous | db.load() |
| Asynchronous | await db.load() |
Internally, the library detects whether it is inside an async event loop.
