Database must know will be: locks and transactions

EDITORIAL

This article is to see the other authors of the excellent blog on the Web, made after his own record for digestion. Based on the article in the MySQL InnoDB storage engine.

Original Bowen Address: Point I

lock

Lock knowledge Overview

We look at a lock of overview and easy follow-up of about:

Database lock - overview

Our program in general can still run properly. Because these lock database implicit help us added; only under certain scenarios, it requires the programmer to manually lock.

  • Before executing the "query" SELECT, it will automatically give all tables add "table-level locking" involved in the read lock ; before performing "update" UPDATE, DELETE, INSERT, automatically to the table add "table-level involved lock "in the write lock

  • For InnoDB , and use the index to "update" UPDATE, DELETE, INSERT statements; then InnoDB will "lock table" is converted to "lock line", which is automatically added to the data set to involve "row-level locking" in the exclusive lock (X)

Note : InnoDB only retrieve data "index" by only using a "row-level locking" Otherwise, InnoDB table lock will be used; that is, InnoDB's row lock-based index .

A special case

If we add a column in the table is "ordinary index", that means: the index column attribute may be repeated .

For the general index, when the repeat rate when, MySQL will not put the general index as the index, which will result in a non-indexed SQL, thereby forming a table lock .

Lock Category

From the figure above, in order to lock granularity of view, we can see that lock into "table-level locking" and "row-level locking"

  • "Table Lock": small overhead, lock fast; not deadlock; locking granularity, high probability of lock conflicts, the lowest degree of concurrency

  • "Line Lock": large overhead, locking slow; there will be a deadlock; lock small size, low probability of lock conflicts, high concurrency

At the same time, different granularity of locking storage engine support is not the same :

  • MyISAM only supports table locks

  • InnoDB row lock and table lock support

Below we take a look at "lock table" and "row lock."

Table lock

For the database, there are two table lock: "Lock the table read" Table Read Lockand "write lock on the table."Table Write Lock

For both locks them there is a blocking situation:

  • Read does not block : the current user to read data, other users can read the data correctly, will not lock

  • Write blocking : the current user to read data, other users can not modify the current user data is read, it will lock

  • Write obstruction : the current user to modify data, other users can not modify the current user is modifying data will be locked

Incompatibilities between them is as follows:

Table lock compatibility

In summary, for table-level locking:

  • Read and write locks exclusive

  • Read and write operations Serial

Row lock

As mentioned earlier, the row lock is 'InnoDB' unique lock, we use MySQL general is the use of "InnoDB" storage engine.

Why is generally used InnoDB

InnoDB and MyISAM There are two essential differences:

  • InnoDB supports row lock

  • InnoDB supports transactions

"Line Lock" and "transaction" allows us to smaller granularity concurrency control, the correct use can improve concurrency database.

InnoDB supports two "row lock" the following:

  • Shared lock

    Also known as the "read lock" or "S lock" is shared, multiple transactions can simultaneously read the same resources, but does not allow other transactions to modify .

    In other words, for the same row, there may be multiple read locks (so you can read at the same time) at the same time; but write locks and read locks are mutually exclusive , and both can not exist (so read the time does not allow other transactions from modifying the same time ).

  • Exclusive lock

    Also known as the "write lock" or "X lock" is exclusive, write lock will block other write locks and read locks , so other transactions can not write data to hold the lock read or write.

Further, in order to allow the coexistence of rows and table locks, multi-granularity locking mechanism , InnoDB there intent lock (Intention Locks) two kinds of internal use, are both intent lock table lock :

  • Intention shared lock IS: that the transaction intends to add rows of data rows shared lock; before a transaction to rows of data shared locks must obtain the IS lock on the table.

  • Intent Exclusive lock IX: that the transaction intends to add rows of data row exclusive lock; affairs before he must first obtain a lock IX lock on the table to add a row of data row

For both intent locks, MySQL will automatically help we get, no need to manually get .


Affairs

"Affairs", we can understand it as an integral part of business operations, such as bank account transfer. We want to improve MySQL concurrent processing capability, in essence, we are hoping to improve the ability of concurrent processing "affairs" of.

In essence, the transaction isolation level is achieved by the lock mechanism , but hides the details of the lock.

Characteristics of the transaction

In order to ensure that the transaction can be handled properly, we need to have the following four provisions of the transaction characteristics (ACID):

  • Atomicity Atomic: each step operation is indivisible transaction, ensure a series of operations are either completed or not completed;

  • Consistency Consistency: before and after the transaction is completed, the state in which the database and business rules must be consistent; for example, a, b account after each transfer, the total balance of both unchanged;

  • Isolation Isolation: isolation from each other transaction operations; this indicates that the transaction must be independent, you should not rely on or affect in any way other transaction;

  • Persistence Durability: Indicates the transaction to the end of the data processing, data changes must be persistent; whether it is successful or roll back the transaction, the transaction log are able to maintain permanent transaction.

Transaction isolation level

Transaction isolation level describes the degree to be isolated, the higher the isolation level, the less subject to interference other things, the concurrent performance worse; relatively high level of isolation to ensure greater security.

There are four general transaction isolation levels, the degree of isolation from low to high, as follows:

  • "Read Uncommitted": read uncommitted; there will be dirty reads, non-repeatable read, phantom read

  • "Read Committed": Read Committed; there will be non-repeatable read, phantom read

  • "Repeatable Read": repeatable read; there will be Magic Reading (MySQL with the gap in the lock GPAto avoid phantom read)

  • "Serializable": Serial; does not appear to be happening, but this time only one after the other transaction execution order

Observation can be found, there is a higher transaction isolation level issues also appear on the lower transaction isolation level, let's analyze the presence of different isolation level from low to high ground problem, and then see how the case is to solve the higher level these issues.

By default, the "MySQL" default isolation level to "Repeatable Reable"; "Oracle" default isolation level is "Read Committed."

"Read Uncommitted" and "dirty read."

Dirty read : refers to a data read transaction another uncommitted transaction

For example: A B intends to transfer, transfer of execution of the transaction, but not yet committed , while B read the data, they found themselves account balance changed much, then B A notice said, "I have received the money"; hypothesis A then perform rollbackthe operation, roll back the transaction, and that B again to view account balances, you will find the money and no increases .

We will B in the middle of reading to your balance increases the data referred to 脏数据, that is, the data is only a state of intermediate data may eventually be converted into another case, not clean and effective; this might read " case dirty "data we called dirty read .

Why is there a "dirty read."

B in reading data when there is no data on the increase "share lock" can lead to read out "exclusive lock" to lock the data.

We look at the top of the dirty read process:

  • B check their balance, that the current balance is x.

  • A transfer execution, that is, modify the account balance of A and B (plus a write lock, but did not commit, that is, data is still locked), assuming a 50 turn to B.

  • See B balances themselves, can be found from A receive transfer (read lock not added, it will read those made in A), the current balance is read x + 50.

  • A transfer roll back the transaction, then A and B account balances to recover before transfer

  • B check their balances, find their balance and changed back (A rollback of the transfer operation, the balance of B's ​​turned into a x).

It can be seen in this case, we will read data from other uncommitted transactions, that is "read uncommitted」Read Uncommitted.

In other cases the isolation level, we can avoid dirty read occurs. But before you learn how to avoid, we need to understand the database "MVCC."

MVCC (Multi-Version Concurrency Control): multi-version concurrency control

The aforementioned problem "dirty reads", in essence, is to read and write in concurrent database read operation may be inconsistent data to be read.

To avoid this, you need concurrent access control database, the easiest way is to "lock access." Since, the serial read and write operations of the lock will, therefore does not appear inconsistent state. However, the read operation will be blocked writes, dramatically reducing the read performance .

In Java concurrent package, there are "copyonwrite" series of classes designed to optimize the reading situation is much larger than writing. And means for its optimization is, during write operation, a data copy, will not affect the original data, then modify, replace atomic swap old data After editing, and read only reads the original data . This is achieved by way of a write will not block read operation to optimize reading efficiency. The operation is to write between mutually exclusive, and each write operation will have a copy, it is only suitable for reading larger than writing .

What is MVCC

"MVCC" principle and "copyonwrite" similar:

Under MVCC agreement, for each "read" will generate a snapshot consistencysnapshot , and use this snapshot to provide a certain level (statement-level or transaction-level) read consistency. From the user's point of view, it seems that the database can provide multiple versions of the same data. You can achieve non-blocking read. MVCC can be considered as an upgraded version of the row-level locking .

MVCC allows data has multiple versions, this version can be based on time stamp or incrementing a global transaction ID, at the same time, we see different transaction data is different.

Contact foregoing "copyonwrite" and "MVCC," we can how to solve the "dirty read" question it?

After Similarly, we can create a copy of the data when data is modified, then changes are complete (that is, the transaction is committed), then replace the old atomic data and its version number; this way, before the transaction commits, the read data is always the same (because the modification of a copy of the data), and after the transaction commits, we can read the new data (updated version after the changes are complete, we can read the latest version of each data). This is also the " Read Committed " solution "dirty read" the.

"Read Committed" and "non-repeatable read"

As already mentioned how "Read Committed" to avoid dirty reads :

Only in the transaction commitonly, will write the data and update the version number; Reads always read the latest version of the data .

So, "Read Committed" of concurrent transactions from accessing process is very clear, we go again in accordance with examples of transfers:

  • B check their balance, that the current balance is x.

  • A transfer execution, that is, modify the account balance of A and B (plus a write lock, but did not commit, that is, data is still locked), assuming a 50 turn to B.

  • B check their balance, reading to balance unchanged (B because the transaction has not yet submitted), as x.

  • A submit transfer transactions , updated version B balances.

  • B view your balance, you can find your own balance of x + 50 (the balance of the latest version of x + 50).

"Read Committed" solves the problem of "dirty read", but it is still there is a corresponding problem.

Non-repeatable read

"Non-repeatable read", as the name suggests, is not to duplicate read consistent data . B, in a transaction, regardless of the current transaction B has no end, other transactions can modify the data B A related query data obtained before that B does not make sense.

In the previous transfer case, B twice their balance inquiry transaction in a query, but because the modification of other transactions lead to inconsistent results, B is the transaction appeared "non-repeatable read" problem.

Why is there a "non-repeatable read"

Read data on the current database statement to obtain a read lock, but after reading it unlocked , regardless of the current transaction has not ended, so allow other affairs of this transaction to modify the data being read, resulting in non-repeatable read.

Therefore, we say "Read Committed" as statement-level snapshot (only to read data plus statement read lock). In the database a higher level of isolation level "Repeatable Read" this problem can be avoided.

"Repeatable Read" and "phantom read."

Speaking of the above, we want to read data in the same transaction are not affected modify other matters, snapshots before then think snapshot; we only need to save a snapshot version number when the transaction open , a time in this transaction reads the corresponding version of the data on it.

In fact, the "Repeatable Read" is to involve the lock rows , and the lock until the end of the transaction is not released , so that we can ensure being able to modify data in this transaction are not being operated other matters, it will not appear " Non-repeatable read the situation "in the.

But also because of its data encryption locks are held for the transaction is level, so "Repeated Read" is called transaction-level snapshots .

Here excerpt Bowen excerpt on how to implement MVCC in InnoDB:

InnoDB-MVCC-1

InnoDB-MVCC-2

Magic Reading

Finally, we need to know, "Repeatable Read" to relate to the data line lock, this also means that, in the case of a transaction table in section A manipulation of data, other transactions can still submit new data to the table, which a result will lead to a transaction twice statistical inconsistencies, as had the same illusion, this is the "phantom read."

Let's use an example to illustrate the two "phantom read":

A transaction has been checked upon insertion of the record does not exist, the data has been surprisingly found that the presence of the detected data acquired before general as hallucinations.

Suppose two panels A and B, after opening, they are user query table contains id = 1the user (id to the primary key of the user table):

-- A 和 B同时执行
SELECT * FROM user WHERE id = 1;

这里假设 user 表为空,因此这里 A 和 B 都查询不到这个用户的存在,所以 A 和 B 都插入一个id = 1的用户数据到 user 表中,我们假设 A 事务先执行,B 事务后执行:

-- A 先执行,B 后执行
INSERT INTO user(id, name) VALUE(1, '张三');

那么在顺序执行的时候,很明显,A 能够成功插入,但 B 插入的时候会产生主键冲突,我们宏观来看是可以理解的,因为 A 已经插入了id = 1的数据;但对于 B 来说,它之前的SELECT语句明明表示不存在,但现在插入的时候却提示已经存在id = 1的数据了,仿佛之前的查询结果是幻觉一样,这就称为「幻读」。

二、同样的条件下,第1次和第2次读出来的表记录数不一样。

假设当前工资为 1000 的有 10 人,事务 1 读取当前工资为 1000 的员工数目,得出结果10条:

-- 结果为 10
SELECT COUNT(*) FROM employee WHERE salary = 1000;

这时事务 2 向员工表中插入一个工资为 1000 的员工信息,并提交:

INSERT INTO employee(name, salary) VALUE('newEmployee', 1000);
COMMIT;

这时事务 2 再次读取员工数目,会得出结果为 11:

-- 结果为 11
SELECT COUNT(*) FROM employee WHERE salary = 1000;

这也就是出现了「幻读」。

怎么解决「幻读」

最简单的解决方式便是让事务「串行」执行,每次同时只能执行一个事务,但这种情况下,我们就无法并行操作事务了。而在「Repeatable Read」的情况下,我们可以给读取的数据加上间隙锁GAP来处理幻读的情况。

间隙锁 GAP

当我们用范围条件检索数据而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合范围条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做「间隙(GAP)」。InnoDB也会对这个间隙加锁,这种锁机制就是所谓的「间隙锁」。

例子:假如emp表中只有101条记录,其empid的值分别是1,2,...,100,101:

SELECT * FROM  emp WHERE empid > 100 for UPDATE;

上面是一个范围查询,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的「间隙」加锁

InnoDB使用间隙锁的目的有两个:

  • 为了防止幻读(上面也说了,「Repeatable Read」隔离级别下再通过GAP锁即可避免了幻读)

  • 满足恢复和复制的需要

    MySQL的恢复机制要求:在一个事务未提交前,其他并发事务不能插入满足其锁定条件的任何记录,也就是不允许出现幻读

值得注意的是:间隙锁只会在「Repeatable Read」隔离级别下使用

乐观锁和悲观锁

我们可以发现,之前的数据库隔离级别,都是为了解决读写冲突问题,我们在默认的「Repeatable Read」的情况下考虑一个问题:

Lost update example of FIG.

上面我们假设,T1~T2时间,张三完成一个查询事务;T3~T6,李四完成一个更新事务;在后序时间段,张三基于上一次的查询进行一个更新事务。在这种情况下,李四的更新就被张三的更新覆盖了,也就是发生了更新丢失

我们可以通过以下方法解决这个问题:

  • 使用Serializable隔离级别,这时事务是串行执行的

  • 乐观锁

  • 悲观锁

乐观锁

「乐观锁」是一种思想,不是数据库层面上的锁,是需要自己手动去加的锁。具体实现是,表中有一个版本字段,第一次读的时候,获取到这个字段。处理完业务逻辑开始更新的时候,需要再次查看该字段的值是否和第一次的一样;如果一样,则更新,并同步更新版本字段;反之拒绝。

之所以叫乐观,因为这个模式没有从数据库加锁,等到更新的时候再判断是否可以更新

具体过程是这样的:

张三select * from table--->会查询出记录出来,同时会有一个version字段

img

李四select * from table--->会查询出记录出来,同时会有一个version字段

img

李四对这条记录做修改:update A set Name=lisi,version=version+1 where ID=#{id} and version=#{version},判断之前查询到的version与现在的数据的version进行比较,同时会更新version字段

此时数据库记录如下:

img

张三也对这条记录修改:update A set Name=lisi,version=version+1 where ID=#{id} and version=#{version},但失败了!因为当前数据库中的版本跟查询出来的版本不一致

img

悲观锁

「悲观锁」是数据库层面加锁,所有更新操作都会阻塞去等待锁。

我们在语句后加FOR UPDATE,即可使用悲观锁:

SELECT * FROM xxx FOR UPDATE

FOR UPDATE等于对选中的行加了「排它锁」,加了写锁之后,其他事务就不能对它进行访问和修改了,这也就保证了不会出现更新丢失的情况。

如何选择乐观锁或悲观锁

两种锁各有优缺点,不可认为一种好于另一种,像「乐观锁」适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。

死锁

并发的问题就少不了死锁,在MySQL中同样会存在死锁的问题。

但一般来说MySQL通过回滚帮我们解决了不少死锁的问题了,但死锁是无法完全避免的,可以通过以下的经验参考,来尽可能少遇到死锁:

  • 固定的顺序访问表和行。比如对两个job批量更新的情形,简单方法是对id列表先排序,后执行,这样就避免了交叉等待锁的情形;将两个事务的sql顺序调整为一致,也能避免死锁。

  • 大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。

  • 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。

  • 降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。

  • 为表添加合理的索引。可以看到如果不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大。

总结

上面说了一大堆关于MySQL数据库锁的东西,现在来简单总结一下。

表锁其实我们程序员是很少关心它的:

  • 在MyISAM存储引擎中,当执行SQL语句的时候是自动加的。

  • 在InnoDB存储引擎中,如果没有使用索引,表锁也是自动加的。

现在我们大多数使用MySQL都是使用InnoDB,InnoDB支持行锁:

  • 共享锁--读锁--S锁

  • 排它锁--写锁--X锁

在默认的情况下,select不加任何行锁的。我们可以通过以下语句显示给记录集加共享锁或排他锁。

  • 共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE

  • 排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE

InnoDB基于行锁还实现了MVCC多版本并发控制,MVCC在隔离级别下的Read committedRepeatable read下工作。MVCC能够实现读写不阻塞

InnoDB achieve Repeatable readisolation level with the gap GAP have avoided phantom read lock.

For the pessimistic locking and optimistic locking, we have summarized as follows:

  • Optimistic locking is actually an idea, as its name implies: that the situation will not update data locked down, if we find something wrong, it does not update (rollback). Often add a version field in the database to achieve.

  • With pessimistic locking is row lock on the database that the database concurrency conflict occurs, the data directly put up locks, other transactions can not be modified until the submission of the current transaction.

Guess you like

Origin www.cnblogs.com/Bylight/p/11850761.html