What Mnesia Is
Mnesia is a distributed, transactional database management system built directly into OTP. It stores native Erlang terms — typically records — as rows, runs inside the same BEAM VM as your application (so there's no separate database server process to install or manage), and can replicate tables across connected nodes. A schema is defined with mnesia:create_table/2, specifying the record's attributes and which nodes hold a copy of the table and in what form.
Cricket analogy: Mnesia running inside the same BEAM process as your application, with no separate database server to manage, is like a captain who also personally keeps the official scorebook during the match rather than relying on a separate scorer sitting in another stand who might lag behind play.
Table Storage Types: ram_copies, disc_copies, disc_only_copies
Each table copy on each node is declared as one of three storage types. ram_copies keeps the table purely in memory for the fastest possible reads and writes, but the data is lost on node restart unless another node's replica survives. disc_copies keeps both a full in-memory copy (for RAM-speed reads) and a disk log, so the table survives a restart without sacrificing read performance. disc_only_copies, backed by dets, stores data only on disk, trading read speed for a much smaller memory footprint — useful for large, infrequently accessed tables.
Cricket analogy: Choosing ram_copies, disc_copies, or disc_only_copies is like deciding which match data to keep on a live scoreboard for instant updates (RAM speed), which to also log in the scorebook nightly (disk-backed but fast), and which decades-old records belong in the slower physical archive room (disk-only).
-record(session, {id, user, node, expires}).
init_schema() ->
mnesia:create_schema([node()]),
mnesia:start(),
mnesia:create_table(session,
[{attributes, record_info(fields, session)},
{ram_copies, [node()]}, %% fast, in-memory only
{index, [#session.user]}]).
write_session(Session) ->
F = fun() -> mnesia:write(Session) end,
mnesia:transaction(F).
lookup_by_user(User) ->
F = fun() ->
mnesia:index_read(session, User, #session.user)
end,
{atomic, Result} = mnesia:transaction(F),
Result.
%% Fast path that skips transaction overhead — no isolation guarantee
fast_lookup(Id) ->
mnesia:dirty_read(session, Id).Transactions and Queries
mnesia:transaction(Fun) executes Fun with full ACID guarantees, using optimistic locking and automatically retrying the transaction if it detects a conflicting concurrent write. For read-heavy or latency-critical paths where strict isolation isn't required, dirty operations like mnesia:dirty_read/1 and mnesia:dirty_write/1 skip the transaction machinery entirely for lower overhead. For queries spanning multiple tables or requiring filtering beyond a simple key lookup, qlc (Query List Comprehension) provides a declarative, SQL-like syntax that Mnesia can optimize.
Cricket analogy: mnesia:transaction/1 guaranteeing all-or-nothing updates is like a DRS review that either overturns the entire on-field decision or confirms it entirely — there's no possibility of a half-overturned, partially-out result left in limbo.
Like the global module, Mnesia has no built-in network-partition tolerance. If a cluster splits, both sides can keep accepting writes independently, and mnesia:merge_schema will surface conflicts (or silently pick one side's data, depending on table type) once connectivity is restored. Also note that disc_only_copies tables are backed by dets, which historically caps a single table file at 2GB — plan capacity or shard accordingly for large disc_only_copies tables.
When to Use Mnesia vs an External Database
Mnesia's strength is near-zero-latency access to structured Erlang data from within the same cluster, which makes it well suited to session tables, routing tables, or live configuration in telecom-style systems where every millisecond of round-trip matters. It's less suited to very large datasets, ad hoc analytical queries, or serving as a durable system of record shared outside the Erlang cluster, where a general-purpose database like PostgreSQL is a better fit — many production systems run both side by side, using Mnesia for hot, transient state and an external database for durable history.
Cricket analogy: This is like a team using a live in-dugout tablet for real-time field placements during play, while leaving decades of career statistics analysis to a dedicated stats department with proper archival tools.
- Mnesia is a distributed, transactional database management system built directly into OTP, storing native Erlang terms as records.
- Tables can be ram_copies (memory only), disc_copies (memory + disk log, RAM-speed reads), or disc_only_copies (disk-backed via dets, lower memory use).
- mnesia:transaction/1 wraps a fun with ACID guarantees, retrying automatically on write conflicts detected via optimistic locking.
- mnesia:dirty_read/1 and other dirty operations skip transaction overhead for speed, at the cost of isolation and consistency guarantees.
- qlc (Query List Comprehension) provides a declarative query syntax that can span multiple Mnesia tables.
- Mnesia has no built-in partition tolerance — a network split can let both sides accept independent writes, requiring manual schema merge/reconciliation.
- Mnesia is best suited to low-latency, structured, cluster-local data (sessions, routing tables, configuration) rather than large-scale analytics workloads.
Practice what you learned
1. Which Mnesia table storage type keeps data only in memory, with no disk persistence?
2. What does mnesia:dirty_read/1 trade away compared to a transactional read via mnesia:transaction/1?
3. What must be called once, before mnesia:start/0, to enable disc-resident tables?
4. What happens by default if a Mnesia cluster experiences a network partition?
5. Which storage type is best suited for large tables where you want to minimize RAM usage at the cost of read speed?
Was this page helpful?
You May Also Like
Erlang and Distributed Systems
Explore how Erlang nodes connect, communicate transparently, and stay resilient across a distributed cluster.
Erlang and Rebar3
Learn how Rebar3 standardizes building, testing, and dependency management for Erlang projects.
Testing Erlang with EUnit
Learn how to write, organize, and run unit tests for Erlang code using the built-in EUnit framework.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics