首頁  >  文章  >  資料庫  >  MySQL中的事務與鎖

MySQL中的事務與鎖

黄舟
黄舟原創
2017-02-06 11:12:541300瀏覽

這篇文章詳細介紹資料庫事務與鎖的相關知識。主要是一些概念性的東西看起來可能比較乏味,但作為一個合格的程式設計師來說,你應該掌握也必須掌握。這些理論知識好比是一個人的內功,我們平常敲程式碼是外功,只有內外兼修,互相促進,才能達到武林高手的境界。好了廢話不多說,從下面開始。

資料庫事務

事務的邊界

事務的開始邊界(begin) 
事務的結束邊界(commit):提交事務,永久保存被事務更新後的資料庫狀態。 
事務的異常結束邊界(rollback):撤銷事務,使資料庫退回到執行事務前的初始狀態。

每啟動一個MySQL.exe程序,就會得到一個單獨的資料庫連線。每個資料庫連線都有一個全域變數autocommit,表示目前的交易模式,它有兩個值可選:

  • 0:表示手動提交模式

  • 1:表示自動提交模式,預設值

1:表示自動提交模式,預設值

我們可以查看和修改這個值。
  • 資料庫事務的4個特性(ACID):

  • 原子性(Atomicity):事務是一個原子操作單元,其對資料的修改,要麼全部執行,要麼全都不一致;

  • 性(Consistent):在事務開始和完成時,資料都必須保持一致狀態;
  • 隔離性(Isolation):資料庫系統提供一定的隔離機制,保證事務在不受外部並發操作影響的“獨立”環境執行;

持久性(Durable):事務完成之後,它對於資料的修改是永久性的,即使出現系統故障也能夠維持。

MySQL中的事務與鎖

事務隔離級別
  • 資料庫事務隔離級別,只是針對一個事務能不能讀取其它事務的中間結果。



  • Read Uncommitted (讀取未提交內容) 

    在該隔離級別,所有事務都可以看到其他未提交事務的執行結果。本隔離等級很少用於實際應用,因為它的效能也不比其他等級好多少。讀取未提交的數據,也稱為髒讀( Dirty Read )。 🎜🎜🎜🎜Read Committed (讀取提交內容) 🎜這是大多數資料庫系統的預設隔離等級(但不是 MySQL 預設的)。它滿足了隔離的簡單定義:一個交易只能看見已經提交事務所所做的改變。這種隔離等級 也支援所謂的不可重複讀取( Nonrepeatable Read ),因為同一交易的其他實例在該實例處理其間可能會有新的 commit ,所以同一 select 可能會傳回不同結果。 🎜
  • Repeatable Read (可重讀) 
    這是 MySQL 的預設事務隔離級別,它確保相同交易的多個實例在並發讀取資料時,會看到相同的資料行。不過理論上,這會導致另一個棘手的問題:幻讀 ( Phantom Read )。簡單的說,幻讀指當用戶讀取某一範圍的數據行時,另一個事務又在該範圍內插入了新行,當用戶再讀取該範圍的數據行時,會發現有新的”幻影” 行。 InnoDB和 Falcon 儲存引擎透過多版本並發控制( MVCC , Multiversion Concurrency Control )機制解決了這個問題。

  • Serializable (可串行化) 
    這是最高的隔離級別,它透過強制事務排序,使其不可能相互衝突,從而解決幻讀問題。簡言之,它是在每個讀取的資料行上加上共享鎖定。在這個級別,可能導致大量的超時現象和鎖定競爭。

隔離等級越高,越能保證資料的完整性和一致性,但對並發效能的影響也越大。 
對於多數應用程序,可以有效考慮把資料庫系統的隔離等級設為Read Committed,它能夠避免髒讀,而且具有較好的並發性能。儘管它會導致不可重複讀、虛度和第二類丟失更新這些並發問題,在可能出現這類問題的個別場合,可以由應用程式採用悲觀鎖和樂觀鎖來控制。

事務的傳播性

  1. PROPAGATION_REQUIRED 
    加入當前正要執行的事務,如果當前事務不存在,那麼就起一個新的事務。 Spring 操作資料庫預設的交易傳播行為就是 propagation_required 。

  2. PROPAGATION_SUPPORTS 
    如果目前在事務中,即以事務的形式運行,如果當前不再一個事務中,那麼就以非事務的形式運行.

  3. PROPAGATION_MANDATORY_MANDATORY也就是說,他只能被一個父事務呼叫。否則,他就要拋出異常。

  4. PROPAGATION_REQUIRES_NEW 

    掛起目前事務,另一個新的交易。

  5. PROPAGATION_NOT_SUPPORTED 

    目前不支援交易。如果在事務中,會掛起目前事務,自己以非事務的行為運作。

  6. PROPAGATION_NEVER 

    不能在事務中運行,如果在事務中運行就會拋出異常。

  7. PROPAGATION_NESTED 

    嵌套的事務依賴父事務,父事務提交,它跟著提交,父事務回滾,它跟著回滾。

行級鎖定

Mysql中三種類型的鎖定:

行級:引擎 INNODB , 單獨的一行記錄加鎖 

頁級:引擎 BDB,一次鎖定相鄰的一組記錄。 
表級:引擎 MyISAM , 理解為鎖住整個表,可以同時讀,寫不行。 
三種鎖的特性大致可歸納如下: 
1) 表級鎖定:開銷小,加鎖快;不會出現死鎖;鎖定粒徑大,發生鎖衝突的機率最高,且發度最低。 
2) 行級鎖定:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖定衝突的機率最低,並發度也最高。 
3) 頁面鎖定:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,並發度一般。

我們這裡主要談論的是行級鎖,一般的在秒殺系統中我們會對商品庫存使用行級鎖,因為秒殺的時候庫存是一個很重要的數據,我們在創建數據庫的表時可能會出現下面這樣的設定:

ENGINE = InnoDB AUTO_INCREMENT=10 DEFAULT CHARACTER SET = utf8 comment='用户表

將引擎設定為InnoDB,InnnoDB與其他引擎的不同:一是支援事務(TRANCSACTION),二是採用了行級鎖定。

InnoDB中两种模式的行级锁:

1)共享锁:允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。 
( Select * from table_name where ……lock in share mode) 
2)排他锁:允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和 排他写锁。(select * from table_name where…..for update)

为了允许行锁和表锁共存,实现多粒度锁机制;同时还有两种内部使用的意向锁(都是表锁),分别为意向共享锁和意向排他锁。

  • 意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。

  • 意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。

注意:InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。应用设计的时候要注意这一点。

行级锁的优缺点

行级锁定的优点:

  • 当在许多线程中访问不同的行时只存在少量锁定冲突。

  • 回滚时只有少量的更改。

  • 可以长时间锁定单一的行。

行级锁定的缺点:

  • 比页级或表级锁定占用更多的内存。

  • 当在表的大部分数据上使用时,比页级或表级锁定速度慢,因为你必须获取更多的锁。如果你在大部分数据上经常进行GROUP BY操作或

者必须经常扫描整个表,比其它锁定明显慢很多。

hibernate中通过行级锁实现的悲观锁。

一些例子:

假设有个表单products ,里面有id跟name二个栏位,id是主键。 
1: 明确指定主键,并且有此条记录,执行row lock。若查无此记录,无lock。

SELECT * FROM products WHERE id='3' FOR UPDATE;SELECT * FROM products WHERE id='3' and name="cat" FOR UPDATE;

2: 无主键,执行table lock。

SELECT * FROM products WHERE name='Mouse' FOR UPDATE;

3: 主键不明确,table lock。

SELECT * FROM products WHERE id<>&#39;3&#39; FOR UPDATE;

注意: FOR UPDATE仅适用于InnoDB,且必须在事务块(BEGIN/COMMIT)中才能生效。此外,如果A与B都对表id进行查询但查询不到记录,则A与B在查询上不会进行row锁,但A与B都会获取排它锁,此时A再插入一条记录的话则会因为B已经有锁而处于等待中,此时B再插入一条同样的数据则会抛出Deadlock found when trying to get lock; try restarting transaction。然后释放锁,此时A就获得了锁而插入成功。

以上就是MySQL中的事务与锁的内容,更多相关内容请关注PHP中文网(www.php.cn)!


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn