100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Erlang and Mnesia

Understand Mnesia, Erlang's built-in distributed database, its table types, transactions, and how it fits into OTP applications.

Practical ErlangIntermediate10 min readJul 10, 2026
Analogies

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).

erlang
-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

Was this page helpful?

Topics covered

#Programming#ErlangStudyNotes#ErlangAndMnesia#Erlang#Mnesia#Table#Storage#StudyNotes#SkillVeris#ExamPrep