2010-06-06 53 views
0

我相信每個人都知道線程的併發感。這個SQL會導致任何問題嗎?

想象在每一個頁面加載以下情形在noobily設置MySQL數據庫:

UPDATE stats SET visits = (visits+1) 

如果一千個用戶在同一時間加載頁面,會自動將計造成任何問題?這是表鎖/行鎖機制嗎?哪一個mysql使用。

+0

應該沒問題。我不寫這個答案,因爲我不完全確定。 – 2010-06-06 05:56:40

回答

3

你有兩個潛在的問題:

  1. 你會得到正確的答案?
  2. 你會得到不合理的鎖定,你的整個應用程序會非常緩慢甚至是死鎖。

正確的答案取決於兩個用戶是否可以計算(訪問+ 1)對相同的訪問值。我們可以想像,數據庫需要做這些動作:

Read visit count 
    Add one to visit count 
    Write visit count 

因此,如果兩個用戶在同一時間他們都可以讀取相同的舊值的工作?這就是交易的隔離級別起作用的地方。正如Artefacto觀察到的默認隔離級別是可重複讀,因此我們得到:

Grab a lock 
Read, increment, Write 
Release lock 

不是

Read (two users same old value) 
Increment 
First user Grab Lock, second waits 
Write 
Release, second user grabs lock 
Write (same value!) 
Release 

然而爭論的水平可能會相當高,很大程度上取決於範圍的交易。假設你有:

Begin transaction 

    Do the visit increment stuff 

    Do some serious business work 

    End transaction <==== visit lock is held until here 

然後你會得到很多人等待訪問鎖定。我們不知道應用程序的整體結構,無論您是否使用這樣的大型事務範圍。很可能你得到了每個SQL語句中單個事務的默認行爲,在這種情況下,爭用只是在SQL語句的持續時間內完成,幾乎和你所希望的一樣。

其他人可能不那麼幸運:有環境(例如Java EE Servlets)隱式事務範圍可以由基礎架構創建,然後我上面顯示的更長時間的事務是默認情況下發生的。更糟糕的是,你的代碼是不是一致寫入的可能性(與訪問增量總是先,或總是最後一個),你可以得到:

Begin transaction 
    Do the visit increment stuff 
    Do some serious business work 
    End transaction <==== visit lock and business locks held until here 

Begin transaction 
    Do some other serious business work 
    Do the visit increment stuff  
    End transaction <==== visit lock and maybesame business locks held until here 

和賓果:死鎖

對於大容量網站,您可以考慮將「訪問」事件寫入隊列,並讓守護進程監聽這些事件並維護計數。更復雜但可能更少的爭用問題。

3

不,這不會搞砸了。這在任何符合ACID的數據庫中都是完全可以接受的。 I代表分離。這些查詢中的每一個都會鎖定訪問表中的所有行。 A(在ACID中)代表原子性並且表示交易必須全部運行或根本不運行。

+0

如果他沒有明確開始交易,該怎麼辦? – 2010-06-06 06:09:19

+0

我不知道有關MySQL中的事務是否對於noobs來說是automagic? – y2k 2010-06-06 06:10:23

+3

@Sayem Ahmed「在InnoDB中,所有的用戶活動都發生在一個事務中,如果啓用了自動提交模式,每個SQL語句都會自行形成一個事務。」 http://dev.mysql.com/doc/refman/5.1/en/innodb-transaction-model.html – Artefacto 2010-06-06 06:11:01

-1

這很好。
所有的「表鎖定/行鎖定」是垃圾數據庫被髮明來照顧。

「千用戶同時加載頁面」可能還有其他問題,如索引更新。但是這是另一個故事,並且不管怎樣,MySQL安裝程序都不是這樣。

1

確保你有SET autocommit,所以這被視爲一個交易,並且數量會很好。唯一的問題是性能(例如具有表熱點)

2

對於MySQL,所述manual說:

[重複讀]爲InnoDB的默認隔離級別。 [...]對於UPDATEDELETE語句,鎖定取決於語句是使用具有唯一搜索條件的唯一索引還是範圍類型搜索條件。對於具有唯一搜索條件的唯一索引,InnoDB只鎖定找到的索引記錄,而不是鎖定之前的缺口。對於其他搜索條件,InnoDB使用間隙鎖或下一個鍵(間隙加索引記錄)鎖來鎖定掃描的索引範圍,以阻止其他會話插入到範圍所覆蓋的間隙中。

所以我會說是的,你很好,雖然那個特定的查詢可能鎖定整個表。它可能會更好:

UPDATE stats SET value = value + 1 WHERE key = 'visits' 

帶有「key」索引。

2

到目前爲止所有的答案似乎假定InnoDB表,它確實支持交易;如果使用MyISAM表,您將獲得「原子事務」,這對您的特定用例應該沒問題(儘管它們遠遠低於一般情況下的完整ACID)。

在上交易MySQL的文檔(如here)它給你的UPDATE形式爲好的做法典型案例,具體地講,我引用...:

這給我們的東西是 類似於列鎖定,但是實際上更好,因爲我們只有 更新了一些列,使用 值與其當前值相關的值爲 。這意味着, 典型的UPDATE語句看起來 的類似:

UPDATE tablename SET pay_back=pay_back+125; 

...這是非常有效的,即使其他客戶端已在pay_back [列]修改的數值,工作

1

這樣,如果你工作:

  1. 在交易
  2. 行鎖定正在運行設置正確

要特別注意第二點。這不是一件容易的事情,因爲MySQL允許你放鬆鎖定約束到一個確實會搞砸的點。另一方面(如果鎖定設置正確),如果你碰到一些非常大的交通流量,這可能會成爲你的瓶頸(因爲它只能在一個單獨的線程中執行)。如果你保持交易的開放時間不僅僅是更新這個數字,這個可能性更大,而且如果你不小心的話,它甚至會導致死鎖,因爲djna會詳細解釋。

0

正如大家所說,如果您使用InnoDB,這將鎖定該行。現在,如果只使用一行,並且該行存儲有關所有訪問的統計信息,那麼在查詢等待獲取鎖時,鎖定此行可能會減慢速度。這種放緩可能在您的負載下無法察覺。如果它很重要,你可以通過寫入多個不同的行來解決它,比如0-255。這仍然會鎖定每一行,但鎖定爭用的機會現在是原來的1/256。當你想要總數時,你可以對所有行進行求和。

UPDATE stats SET value=value+1 WHERE id=X 

,其中X是一個隨機編號0-255

然後

SELECT SUM(value) FROM stats 

會給你真正的總。

相關問題