picostitch
crafting (and) JavaScript

LMDB – A Nice key-value Store?

LMDB (Lightning Memory-Mapped Database) is a key-value database designed for speed and efficiency. Ok, which DB does not say that.
It is particularly well-suited for applications that require fast read and write operations, its speed is close to what the OS can provide.

I am currently learning about LMDB. The blog post was triggered by the marketing I fall for too often, the "fastest" "most efficient" etc. Since I am skeptic by nature, I always dig for the drawbacks and let me give you the real picture about LMDB here. I always try to understand the tech behind everything to create a picture of it myself, so here is my take on LMDB.

They Say

First, the site is still http 😱! http://www.lmdb.tech/doc/index.html Ouch.

They claim on the above-linked tech docs:

[...] all data fetches return data directly from the mapped memory, so no malloc's or memcpy's occur during data fetches.
[...] extremely simple
[...] no page caching layer
[...] extremely high performance and memory-efficient.
[...] fully transactional with full ACID semantics
[...] integrity cannot be corrupted

I could go on. To be fair, the next paragraph is headed "Caveats", though it is very technical.

The Reality

LMDB is a key-value store. It does not support SQL queries or complex joins. It has an amazing architecture, I agree. Quite clever to basically just write directly to the places in memory, that just map to the disk and let the OS do the rest. (Saying it in my definitely not expert words.)

Given the rough sketch of the architecture, it is clear that LMDB has shortcomings. Let me dig into some questions that come up while I read this.

Reads and Writes never block — How?

LMDB uses a memory-mapped file to store data, which allows it to access data directly in memory without copying it around. That makes sense, the read just points there and the application layer does with the data what it wants, reading it multiple times is no issue.
But writing and not blocking? That can only mean, either that this causes eventual inconsistencies, so a read might read old data, or it does some magic.

ChatGPT gave me this answer:

LMDB allocates new pages, writes updates there, and commits them with an atomic pointer swap

Assuming this is correct, this is awesome but also no magic. It is just quite clever, flipping the pointer on the lowest layer possible cannot be anything but amazing fast. I love it.

Reads are potentially inconsistent — Right?

I guess "eventually consistent" is the right term here.

As seen above, writes need a bit and once available are immediately visible to all readers. But in the meantime, readers might read old data, right? This was my next guess, and curiously I asked ChatGPT again:

me: reads are not ensured to always get the latest, but just eventually
ChatGPT: Yes — exactly.

Writes are blocking — Right?

Knowing that writes potentially take a bit of time (allocate page, write, commit), writes must be sequential and blocking. In the docs they say:

Writes are fully serialized; only one write transaction may be active at a time, which guarantees that writers can never deadlock

The no deadlock thing is definitely cool; it also ensures the other feature they mention, the DB is crash-safe. Let it crash and reboot, and it just works again, no need to repair or so.

So yes, writes are blocking. One thing to know about the architecture. And seeing the context, where this project comes from the OpenLDAP project, it makes sense to have a DB that is fast and crash-safe and most probably not that write-heavy.

Heavy Writes might Become a Bottleneck?

Knowing what we learned until now, there is no way around this. Writes are blocking, and if you have a lot of writes you will have to wait for them to finish.

So, write-heavy applications might not be the best fit for LMDB. Though I assume it will perform way better than most other DBs.

Data Are not Cross-Platform?

LMDB uses a binary format to store data, which means that data written on one platform may not be readable on another platform. I don't think this is the biggest issue, but makes it very clear how the underpinnings work. I thought of that question when Howard Chu, the author of LMDB mentioned in this video that he is thinking about storing even bigger native structures in the DB as they are. Also quite clever.

I am sure data can be ex-/imported somehow if needed.

Finally

I am glad I took the 2h to digg deeper into LMDB. I guess it is very nish and quite special, but if you have the right use case, it is a great fit.

One concern I have is that it looks a bit less maintained, especially little has happened since 2018 see the contributions on github. But it might also just be done and perfect.