Talking about MySQL transaction and ACID

I recently built a personal blog, and the link is here: Tobe's ravings , the article will be updated on the blog and official account first~ I hope you will like it a lot.

The so-called transaction (Transaction) is to maintain the integrity of the database by ensuring that batches of operations are either fully executed or not executed at all. Take an example of a bad street: A transfers 1,000 yuan to B, and the corresponding SQL statement is: (no transaction is explicitly defined)

UPDATE deposit_table set deposit = deposit - 1000 WHERE name = 'A';
UPDATE deposit_table set deposit = deposit + 1000 WHERE name = 'B';

The result after running is as follows:

mysql> SELECT * FROM deposit_table;
+------+---------+
| name | deposit |
+------+---------+
| A    |    3000 |
| B    |    5000 |
+------+---------+

Doing so may encounter problems, such as the database crash after the first statement is executed, and the final result may be like this ( after all, we will not simulate this kind of failure ):

+------+---------+
| name | deposit |
+------+---------+
| A    |    2000 |
| B    |    5000 |
+------+---------+

A's 1000 yuan disappeared for no reason, which is certainly not appropriate. Transactions appear to solve similar problems. If transactions are used to process transfers, the corresponding SQL is:

START TRANSACTION;
UPDATE deposit_table set deposit = deposit - 1000 WHERE name = 'A';
UPDATE deposit_table set deposit = deposit + 1000 WHERE name = 'B';
COMMIT;

Just adding START TRANSACTIONand COMMITcan ensure that even if the transfer operation fails, the balance of A will not decrease.

Think about it carefully and find that this example is not particularly suitable, because the failure recovery technology of the database (will be discussed later) will affect the final result, and it is not easy to simulate this kind of failure, the final result can only be guessed :) But I can't think of it either other more appropriate examples. . . If you have better examples, please leave a message for discussion.

Some of the features and (some) implementation details of transactions are discussed in detail next.

ACID

  • A: Atomicity (atomicity)
  • C: Consistency
  • I: Isolation (isolation)
  • D: Durability

Atomicity

Let’s talk about two important concepts first: commit and rollback . When we perform a commit operation, the database will be permanently modified. Performing a rollback operation means that the database will undo all ongoing Submitted changes . Note that the permanence here does not mean that the data is flushed to the disk as soon as the transaction is completed. Even if it is not flushed to the disk, MySQL also has a logging mechanism to ensure that the modification will not be lost.

A transaction is a unit of work that supports commit and rollback . Atomicity means that when a transaction makes multiple changes to the database, either all changes are successful when the transaction is committed, or all changes are undone when the transaction is rolled back . This is the statement of the official document , but some people seem to misunderstand the commit statement. In fact, even if there is an error in a statement in the transaction, once you execute the commit, the previous normal changes will still be submitted, and MySQL will not automatically judge . Whether the SQL in the transaction was executed successfully or not.

Let's take a look at commit and rollback with an example next:

mysql> SELECT * FROM deposit_table;
+------+---------+
| name | deposit |
+------+---------+
| A    |    2000 |
| B    |    6000 |
+------+---------+
2 rows in set (0.04 sec)

mysql> 
START TRANSACTION;
INSERT INTO deposit_table VALUES('C', 7000);
INSERT INTO deposit_table VALUES('D', 8000);
#再次插入 D,由于主键的唯一性,该语句会执行失败
INSERT INTO deposit_table VALUES('D', 9000);
COMMIT; #提交事务

Query OK, 0 rows affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

1062 - Duplicate entry 'D' for key 'PRIMARY'
Query OK, 0 rows affected (0.07 sec)
mysql> SELECT * FROM deposit_table;
+------+---------+
| name | deposit |
+------+---------+
| A    |    2000 |
| B    |    6000 |
| C    |    7000 |
| D    |    8000 |
+------+---------+
4 rows in set (0.04 sec)

We can see that when INSERT INTO deposit_table VALUES('D', 9000)executing , since D has been inserted into the previous statement, this SQL statement fails to execute and an 1062 - Duplicate entry 'D' for key 'PRIMARY'error is reported, but COMMITafter , the previous modification is still submitted, which is obviously not in line with us expected.

Note: If you are using Navicat's query interface, the COMMITstatement , but only to the place where the error is reported. It is recommended to use the command line to execute.

So in practice, we need to determine whether to use ROLLBACKor COMMIT. like this:

# 创建一个存储过程
CREATE DEFINER=`root`@`localhost` PROCEDURE `insert_test`()
BEGIN
	# 创建一个标志符,出现错误就将其置为 1
	DECLARE err_flg INTEGER;
    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET err_flg = 1;

    START TRANSACTION;
        INSERT INTO deposit_table VALUES('C', 7000);
				INSERT INTO deposit_table VALUES('D', 8000);
				INSERT INTO deposit_table VALUES('D', 9000);
        
        # 发生错误,回滚事务
        IF err_flg = 1 THEN
			SELECT 'SQL Err Invoked'; # 错误提示信息
            ROLLBACK;
			SELECT * FROM deposit_table;
		# 没有发生错误,直接提交
        ELSE
			SELECT 'TRANSACTION Success';
            COMMIT;
			SELECT * FROM deposit_table;
        END IF;
	
END

Next we call the stored procedure:

mysql> call insert_test();
+-----------------+
| SQL Err Invoked |
+-----------------+
| SQL Err Invoked |
+-----------------+
1 row in set (0.04 sec)

+------+---------+
| name | deposit |
+------+---------+
| A    |    2000 |
| B    |    6000 |
+------+---------+
2 rows in set (0.09 sec)

Query OK, 0 rows affected (0.00 sec)

In the result, the error message is printed and the content of the SQL Err Invokedtable has not changed, indicating that our ROLLBACK successfully rolled back the transaction, which met our expectations. If you are using other languages ​​to call the MySQL interface, you only need to get the error flag and execute ROLLBACKor COMMIT.

Consistency

The explanation given on the official website is as follows:

The database remains in a consistent state at all times — after each commit or rollback, and while transactions are in progress. If related data is being updated across multiple tables, queries see either all old values or all new values, not a mix of old and new values.

The translation is: after each commit or rollback, and during an ongoing transaction, the database is always in a consistent state, and if related data is updated across multiple tables, the query will see all old values ​​or all new values, while Not a mix of old and new values .

for example:

# 表 a,b 的定义略过
START TRANSACTION;
UPDATE a SET name = 'a_new' WHERE name = 'a_old';
UPDATE b SET name = 'b_new' WHERE name = 'b_old';
COMMIT;

Consistency in this example means that if there is a query at this time SELECT a.name, b.name FROM a, b;, the result is either a_old b_old (indicating that the transaction has been rolled back or is executing) or a_new b_new(indicating that the transaction has been successfully committed), a_old b_newand a_new b_oldoccur .

Some blogs interpret consistency as "data conforms to constraints in the real world , such as uniqueness constraints, etc." I personally prefer the interpretation of official documents, which is a matter of opinion, and it is not meaningful to entangle these concepts.

Isolation

Transaction isolation means that transactions cannot interfere with each other and cannot see each other's uncommitted data . This isolation is achieved through a locking mechanism. We have also learned in the operating system that the use of locks often means a decrease in concurrent performance, because blocking or even deadlock may occur.

Of course, when users determine that transactions do not interfere with each other, they can adjust the isolation level and sacrifice part of the isolation to improve performance and concurrency . As for which isolation level ( isolation level ) to use, you need to do trade off yourself.

Because isolation involves a lot of content, I will explain it in detail in the next article.

Durability

The durability of the transaction means that once the commit operation is successful, the changes made by the transaction will not be lost due to some accidents, such as potential threats such as power failure and system crash. MySQL provides many mechanisms, such as logging technology, doublewrite buffer and so on.

MySQL's log recovery technology I will write a separate article, here to talk about doublewrite buffer technology.

Although the technical name is buffer, the buffer is not actually located in memory, but on disk . This may sound weird - since you're putting data on disk, why not write it directly to the data file, instead of doing it?

This is because the Page Size of InnoDB is generally 16kb, and its data checksum is also calculated for the page. During the process of flushing the data to the disk, if there is a failure such as a power failure, only a part of the page may be written (partial page). write). This situation cannot be solved by the redo log, because the physical operations on the page are recorded in the redo log. If the page itself is damaged, it is meaningless to redo it. So we need a copy to restore the page when this happens.

Moreover, the buffer is written sequentially, and the overhead is much smaller than random read and write, so after doublewrite, the performance is not reduced to 50% of the original.

Common Statements in Transactions

  • START TRANSACTION / BEGIN explicitly starts a transaction

  • COMMIT commits the transaction, permanently modifying the database

  • SAVEPOINT creates a savepoint within a transaction

  • RELEASE SAVAPOINT removes a savepoint

  • ROLLBACK rolls back the transaction, withdraws all uncommitted changes, and terminates the transaction

  • ROLLBACK TO [SAVEPOINT] rolls back to a given savepoint, but the transaction is not terminated. In addition, the row lock after the savepoint will not be released , see SAVEPOINT, ROLLBACK TO SAVEPOINT, and RELEASE SAVEPOINT Statements for details :

    ​ InnoDB does not release the row locks that were stored in memory after the savepoint. (For a new inserted row, the lock information is carried by the transaction ID stored in the row; the lock is not separately stored in memory. In this case, the row lock is released in the undo.)

  • SET TRANSACTION Set transaction isolation level

  • SET autocommit 0/1 Whether to commit automatically (default auto-commit)

Emphasize the autocommit parameter. By default, if not explicitly used START TRANSACTION / BEGIN, MySQL will treat each statement of SQL as a separate transaction, for example:

The original table structure:

mysql> SELECT * FROM deposit_table;
+------+---------+
| name | deposit |
+------+---------+
| A    |    2000 |
| B    |    6000 |
+------+---------+
2 rows in set (0.04 sec)

New stored procedure (just removed START TRANSACTION):

CREATE DEFINER=`root`@`localhost` PROCEDURE `insert_test`()
BEGIN
	#Routine body goes here...
	DECLARE err_flg INTEGER;
    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET err_flg = 1;

    # START TRANSACTION;
    INSERT INTO deposit_table VALUES('C', 7000);
	INSERT INTO deposit_table VALUES('D', 8000);
	INSERT INTO deposit_table VALUES('D', 9000);
        
        IF err_flg = 1 THEN
			SELECT 'SQL Err Invoked';
            ROLLBACK;
			SELECT * FROM deposit_table;
        ELSE
			SELECT 'TRANSACTION Success';
            COMMIT;
			SELECT * FROM deposit_table;
        END IF;
	
END

The result of the call:

mysql> call insert_test();
+-----------------+
| SQL Err Invoked |
+-----------------+
| SQL Err Invoked |
+-----------------+
1 row in set (0.24 sec)

+------+---------+
| name | deposit |
+------+---------+
| A    |    2000 |
| B    |    6000 |
| C    |    7000 |
| D    |    8000 |
+------+---------+
4 rows in set (0.28 sec)

Query OK, 0 rows affected (0.21 sec)

Here we see that C and D are still inserted, although ROLLBACK is indeed executed deposit_table. This is because there is no explicit transaction, MySQL will perform an implicit transaction, automatically committing each modification, so it cannot be rolled back.


That's all for the basic concepts of transactions. In the future, I will talk about the isolation mechanism of transactions, paradigm design, etc., so stay tuned!

I hope you gain something after reading my article, and look forward to your likes and reposts!

If this article is helpful to you, welcome to pay attention to the ravings of my public account tobe , and take you deep into the world of computers~ There are surprises in the background of the public account to reply to the keyword [computer]~

{{o.name}}
{{m.name}}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324087008&siteId=291194637