Erlang OTP: Distributed & Fault-Tolerant Systems Programming · บทเรียน

ธุรกรรมและการจัดการข้อมูล

ดำเนินธุรกรรมแบบอะตอมบนตาราง Mnesia เพื่อให้ข้อมูลมีความสอดคล้องกันทั่วทั้งโหนดแบบกระจาย

บทเรียน 2 จาก 410 ขั้นตอน

ธุรกรรมและการจัดการข้อมูล เป็นบทเรียน Erlang OTP: Distributed & Fault-Tolerant Systems Programming ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Erlang OTP: Distributed & Fault-Tolerant Systems Programming และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Erlang OTP: Distributed & Fault-Tolerant Systems Programming มีบทเรียนทั้งหมด 4 บทเรียน

บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ

Why Mnesia Needs Transactions

Imagine managing important data like bank transfers. You wouldn't want money to leave one account without arriving in another, right?

This "all or nothing" principle is crucial for data consistency, and it's where transactions come in. Mnesia uses transactions to group multiple database operations into a single, atomic unit.

An atomic transaction either fully completes all its operations, or if any part fails, all changes are rolled back. This ensures your data remains consistent and reliable.

The Core: `mnesia:transaction`

In Mnesia, you perform transactional operations using the mnesia:transaction/1 function. It takes an anonymous function (a fun) as its argument.

All Mnesia read and write operations that need to be atomic must be placed inside this fun. If the fun executes successfully, the changes are committed. If an error occurs or mnesia:abort/1 is called, all changes are rolled back.

Let's see its basic structure:

-module(example_transaction).
-export([main/0]).

main() ->
    mnesia:start(),
    {atomic, _Result} = mnesia:transaction(fun() ->
        % Mnesia operations go here
        io:format("Inside the transaction!~n"),
        ok
    end),
    io:format("Transaction completed.~n"),
    mnesia:stop().

Preparing Data for Transactions

Before we can manipulate data, we need a Mnesia table. Let's define a simple person record and create a table for it. This setup will be used in our transaction examples.

Remember, mnesia:create_table/2 is usually called once when your application starts up for the first time.

-module(mnesia_table_setup).
-export([main/0]).
-record(person, {id, name, age}).

main() ->
    % Ensure Mnesia is started on this node
    mnesia:create_schema([node()]),
    mnesia:start(),
    % Create table if it doesn't exist
    case mnesia:create_table(person, [{attributes, record_info(fields, person)}]) of
        {atomic, ok} -> io:format("Table 'person' created successfully.~n");
        {aborted, {already_exists, person}} -> io:format("Table 'person' already exists.~n");
        Error -> io:format("Error creating table: ~p~n", [Error])
    end,
    mnesia:stop().

Adding & Updating Records

Inside a transaction, you use mnesia:write/1 to store or update records. If a record with the same primary key (the first field in our person record, id) already exists, it will be updated. Otherwise, a new record is inserted.

Let's add a new person to our table. Remember to start Mnesia first!

-module(write_example).
-export([main/0]).
-record(person, {id, name, age}).

main() ->
    % Ensure Mnesia is started and table exists (simplified setup)
    mnesia:create_schema([node()]),
    mnesia:start(),
    mnesia:create_table(person, [{attributes, record_info(fields, person)}]),
    mnesia:wait_for_tables([person], 5000),

    % Write a new person record
    Person1 = #person{id = 1, name = "Alice", age = 30},
    {atomic, ok} = mnesia:transaction(fun() ->
        mnesia:write(Person1)
    end),
    io:format("Wrote: ~p~n", [Person1]),

    mnesia:stop().

Fetching Records Transactionally

To retrieve data inside a transaction, use mnesia:read/1. It takes a record or a record key (like {person, 1}) and returns a list of matching records. If no record is found, it returns [] (an empty list).

Let's read the person we just added. We'll combine writing and reading in a single transaction for a fuller example.

-module(read_example).
-export([main/0]).
-record(person, {id, name, age}).

main() ->
    mnesia:create_schema([node()]),
    mnesia:start(),
    mnesia:create_table(person, [{attributes, record_info(fields, person)}]),
    mnesia:wait_for_tables([person], 5000),

    Person1 = #person{id = 1, name = "Alice", age = 30},
    Person2 = #person{id = 2, name = "Bob", age = 25},

    {atomic, Result} = mnesia:transaction(fun() ->
        % Write two records
        mnesia:write(Person1),
        mnesia:write(Person2),
        % Read one of them
        mnesia:read({person, 1})
    end),
    io:format("Transaction result (read data): ~p~n", [Result]),

    mnesia:stop().

Modifying Data with Transactions

mnesia:write/1 handles both inserting new records and updating existing ones. To remove a record entirely, you use mnesia:delete/1, providing the full record or just its key.

Let's update Alice's age and then delete Bob from our Mnesia table, all within a single, consistent transaction.

-module(update_delete_example).
-export([main/0]).
-record(person, {id, name, age}).

main() ->
    mnesia:create_schema([node()]),
    mnesia:start(),
    mnesia:create_table(person, [{attributes, record_info(fields, person)}]),
    mnesia:wait_for_tables([person], 5000),

    % Ensure initial data for update/delete
    mnesia:transaction(fun() ->
        mnesia:write(#person{id = 1, name = "Alice", age = 30}),
        mnesia:write(#person{id = 2, name = "Bob", age = 25})
    end),

    io:format("--- Before transaction ---~n"),
    io:format("Alice: ~p~n", [mnesia:dirty_read({person, 1})]),
    io:format("Bob: ~p~n", [mnesia:dirty_read({person, 2})]),

    {atomic, _} = mnesia:transaction(fun() ->
        % Update Alice's age
        UpdatedAlice = #person{id = 1, name = "Alice", age = 31},
        mnesia:write(UpdatedAlice),
        % Delete Bob
        mnesia:delete({person, 2})
    end),

    io:format("--- After transaction ---~n"),
    io:format("Alice: ~p~n", [mnesia:dirty_read({person, 1})]),
    io:format("Bob: ~p~n", [mnesia:dirty_read({person, 2})]), % Should be []

    mnesia:stop().

Transaction Rollbacks in Action

The power of transactions lies in their atomicity. If any operation within the fun fails or you explicitly call mnesia:abort/1, Mnesia will roll back all changes made during that transaction.

This means your database state will revert to how it was before the transaction started, preventing partial updates and maintaining consistency.

-module(rollback_example).
-export([main/0]).
-record(person, {id, name, age}).

main() ->
    mnesia:create_schema([node()]),
    mnesia:start(),
    mnesia:create_table(person, [{attributes, record_info(fields, person)}]),
    mnesia:wait_for_tables([person], 5000),

    % Ensure Alice exists initially
    mnesia:transaction(fun() ->
        mnesia:write(#person{id = 1, name = "Alice", age = 30})
    end),
    io:format("Initial Alice: ~p~n", [mnesia:dirty_read({person, 1})]),

    % Attempt a transaction that will abort
    Result = mnesia:transaction(fun() ->
        mnesia:write(#person{id = 1, name = "Alice", age = 35}), % Update
        mnesia:write(#person{id = 3, name = "Charlie", age = 22}), % New record
        mnesia:abort("Something went wrong!") % Abort the transaction
    end),
    io:format("Transaction result: ~p~n", [Result]),

    io:format("Alice after aborted transaction: ~p~n", [mnesia:dirty_read({person, 1})]),
    io:format("Charlie after aborted transaction: ~p~n", [mnesia:dirty_read({person, 3})]),

    mnesia:stop().

`mnesia:sync_transaction/1` for Global Commit

While mnesia:transaction/1 ensures local atomicity, its return doesn't guarantee the transaction has committed on all Mnesia replicas in a distributed setup. For that, you use mnesia:sync_transaction/1.

mnesia:sync_transaction/1 blocks the caller until the transaction has been successfully committed on all nodes where the affected tables are resident. This is crucial for strong consistency guarantees in distributed systems.

Use it when you absolutely need to know that data is consistent across your entire cluster before proceeding.

-module(sync_transaction_example).
-export([main/0]).
-record(item, {id, name}).

main() ->
    % This example assumes a distributed Mnesia setup.
    % For a single node, it behaves like mnesia:transaction/1.
    mnesia:create_schema([node()]),
    mnesia:start(),
    mnesia:create_table(item, [{attributes, record_info(fields, item)}]),
    mnesia:wait_for_tables([item], 5000),

    Item = #item{id = 101, name = "Widget A"},

    io:format("Attempting sync_transaction...~n"),
    {atomic, ok} = mnesia:sync_transaction(fun() ->
        mnesia:write(Item)
    end),
    io:format("Item written and committed across all replicas: ~p~n", [Item]),

    mnesia:stop().

Transaction Knowledge Check

You've learned about Mnesia transactions and how to manipulate data atomically. Let's test your understanding!

Transactions: Your Data's Safety Net

In this lesson, you mastered Mnesia transactions, a cornerstone for building robust and reliable applications.

  • We learned that transactions provide atomicity, ensuring "all or nothing" data operations.
  • You practiced using mnesia:transaction/1 to group Mnesia operations.
  • We covered mnesia:write/1 for adding/updating, mnesia:read/1 for fetching, and mnesia:delete/1 for removing records.
  • We briefly touched upon mnesia:sync_transaction/1 for strong distributed consistency.

Next, we'll dive into configuring Mnesia for distributed operation, including data replication and fault-tolerant storage across a cluster.

เริ่มต้นได้ฟรี

เรียนรู้ Erlang ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
12
บทเรียน
48

คำถามที่พบบ่อย

บทเรียน “ธุรกรรมและการจัดการข้อมูล” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ธุรกรรมและการจัดการข้อมูล” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Erlang OTP: Distributed & Fault-Tolerant Systems Programming ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Erlang OTP: Distributed & Fault-Tolerant Systems Programming มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ธุรกรรมและการจัดการข้อมูล”

ดำเนินธุรกรรมแบบอะตอมบนตาราง Mnesia เพื่อให้ข้อมูลมีความสอดคล้องกันทั่วทั้งโหนดแบบกระจาย คุณปฏิบัติ Erlang OTP: Distributed & Fault-Tolerant Systems Programming ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Erlang OTP: Distributed & Fault-Tolerant Systems Programming หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Erlang OTP: Distributed & Fault-Tolerant Systems Programming บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “ธุรกรรมและการจัดการข้อมูล” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Erlang OTP: Distributed & Fault-Tolerant Systems Programming นี้ได้ไหม

ได้ บทเรียน Erlang OTP: Distributed & Fault-Tolerant Systems Programming ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. พื้นฐานและสคีมาของ Mnesia
  2. ธุรกรรมและการจัดการข้อมูล
  3. Mnesia แบบกระจายและการทำสำเนาข้อมูล
  4. การทำดัชนี Mnesia และการปรับคำค้นให้เหมาะสม
← กลับไปที่ Erlang OTP: Distributed & Fault-Tolerant Systems Programming