Databases written in Rust
sled
sled
⮳ is a high-performance, fairly low-level, embedded database. It can be thought of as a BTreeMap<[u8], [u8]>
that stores its data on disk.
- Storage on disk, without dealing with files
- No external database
- No network costs
- API similar to a thread-safe
BTreeMap<[u8], [u8]>
- Serializable (ACID) transactions for atomically reading and writing to multiple keys in multiple keyspaces
- Fully atomic single-key operations, including compare and swap
- Zero-copy reads
- Write batches
- Subscription to changes on key prefixes
- Multiple keyspaces
- Merge operators
- Forward and reverse iterators over ranges of items
- Crash-safe monotonic ID generator capable of generating 75-125 million unique ID's per second
zstd
⮳ compression (use the compression build feature, disabled by default)- CPU-scalable, lock-free implementation
- Flash-optimized log-structured storage
- Uses modern b-tree techniques such as prefix encoding and suffix truncation for reducing the storage costs of long keys with shared prefixes. If keys are the same length and sequential then the system can avoid storing 99%+ of the key data in most cases, essentially acting like a learned index
fn main() -> anyhow::Result<()> { // Open a sled database // A directory will be created if it does not exist let db: sled::Db = sled::open("temp/my_db")?; // Insert a key to a new value, returning the last value if it was set assert_eq!(db.insert(b"key1", b"Hello, Sled!"), Ok(None)); // Retrieve a value, if it exists assert_eq!(&db.get(b"key1")?.unwrap(), b"Hello, Sled!"); // Note: under the cover, `get` returns an `IVec`, which is a data structure // that makes some things more efficient. IVec implements `AsRef<[u8]>`, // so we can use `&` above assert_eq!(db.get(b"key1")?, Some(sled::IVec::from(b"Hello, Sled!"))); // `key2` does not exist assert_eq!(db.get(b"key2")?, None); // `sled` is fully thread-safe, and all operations are atomic // Atomic compare-and-swap, capable of unique creation, conditional // modification, or deletion // - If old is `None`, it will only set the value if it doesn't exist yet // - If new is `None`, it will delete the value if old is correct // - If both old and new are `Some`, it will modify the value only if old is // correct db.compare_and_swap( b"key1", // key Some(b"Hello, Sled!"), // old value Some(b"Hey"), // new value, None for delete )??; // Remove the key-value pair let old_value = db.remove(b"key1")?; assert_eq!(old_value, Some(sled::IVec::from(b"Hey")),); assert_eq!(db.get(b"key1"), Ok(None)); // Perform a multi-key serializable transaction: // for example, use write-only transactions as a writebatch db.transaction::<_, _, sled::Error>(|tx_db| { tx_db.insert(b"key3", b"stuff")?; tx_db.insert(b"key4", b"much")?; Ok(()) })?; assert_eq!(&db.get(b"key3")?.unwrap(), b"stuff"); assert_eq!(&db.get(b"key4")?.unwrap(), b"much"); // Multiple Trees with isolated keyspaces are supported with the // `Db::open_tree` method. let other_tree: sled::Tree = db.open_tree(b"another tree")?; other_tree.insert(b"k1", &b"value"[..])?; db.drop_tree(b"another tree")?; // Synchronously flushes all dirty IO buffers and calls fsync. // If this succeeds, it is guaranteed that all previous writes will be // recovered if the system crashes. Returns the number of bytes flushed // during this call. `flush_async` is also available. db.flush()?; print!("`sled` example ran successfully."); Ok(()) }
SurrealDB
SurrealDB is a scalable, distributed, collaborative, document-graph database, for the realities web.
use serde::Deserialize; use serde::Serialize; use surrealdb::Result; // Database client instance for embedded or remote databases: use surrealdb::Surreal; // For an in-memory database: use surrealdb::engine::local::Mem; // For a RocksDB file: // use surrealdb::engine::local::RocksDb; // SurrealDB is a scalable, distributed, collaborative, // document-graph database for the realtime web. // The `surrealdb` crate can be used to start an embedded in-memory // datastore, an embedded datastore persisted to disk, // a browser-based embedded datastore backed by IndexedDB, // or for connecting to a distributed TiKV key-value store. // Add `surrealdb = { version = "2.1.4", features = [ "kv-mem" ] }` // to `Cargo.toml` for an in-memory datastore. // The document to store in the database #[derive(Debug, Serialize, Deserialize)] struct Person { name: String, // or: Cow<'static, str> age: u16, } #[tokio::main] async fn main() -> Result<()> { // Initialize a SurrealDB in-memory instance. let db = Surreal::new::<Mem>(()).await?; // Alternatively, connect to a local endpoint: // let db = Surreal::new::<Ws>("localhost:8000").await?; // Or create database connection using RocksDB: // let db = Surreal::new::<RocksDb>("path/to/database-folder").await?; // Select a namespace and database. db.use_ns("test_namespace").use_db("test_db").await?; // Create a record with a specific ID let created: Option<Person> = db .create(("person", "John Doe")) .content(Person { name: "J. Doe".to_string(), age: 42, }) .await?; println!("Created person: {:?}", created); // Select all person records. let people: Vec<Person> = db.select("person").await?; println!("All people: {:?}", people); // Find a specific person by name using a WHERE clause. // Demonstrates how to use `query()` with bindings to prevent SQL injection // vulnerabilities. Note that the `query()` method is able to hold more // than one statement. let mut result = db .query("SELECT * FROM person WHERE name = $name") .bind(("name", "J. Doe")) .await?; let people: Vec<Person> = result.take(0)?; println!("Specific person: {:?}", people); // Update a person's age. let _updated: Option<Person> = db.update(("person", "John Doe")) // Assumes that the name is unique for simplicity. In real applications, use a proper ID. .content(Person { name: "J. Doe".to_string(), age: 43, }) .await?; // Delete a person. let deleted: Option<Person> = db.delete(("person", "John Doe")).await?; println!("Deleted person: {:?}", deleted); // Check if the person exists after deletion. let people_after_delete: Vec<Person> = db.select("person").await?; println!("People after delete: {:?}", people_after_delete); Ok(()) }