假設我們有一個庫存系統,可以跟蹤商店中可用的產品數量(數量)。所以我們可以有類似的東西:
| ID | 姓名 | 數量 |
|---|---|---|
| 1 | 筆記本電腦 | 10 |
這里我們需要考慮兩件事:
- 確保這
Quantity永遠不會是負面的 - 如果我們同時有對產品的請求,我們必須確保有效
Quantity。
換句話說,我們可以有:
request15 臺筆記本電腦(此請求將在 上處理thread1)request2用于 1 臺筆記本電腦(此請求將在 上處理thread2)
當兩個請求都被處理時,資料庫應該包含
| ID | 姓名 | 數量 |
|---|---|---|
| 1 | 筆記本電腦 | 4 |
然而,情況可能并非如此,這取決于我們如何撰寫代碼。如果在我們的服務器上我們有類似的東西:
var product = _database.GetProduct();
if (product.Quantity - requestedQuantity >= 0)
{
product.Quantity -= requestedQuantity;
_database.Save();
}
使用此代碼,兩個請求(在單獨的執行緒上執行)可能會同時到達代碼的第一行。
thread1:_database.GetProduct(); //數量為10thread2:_database.GetProduct(); //數量為10thread1: _product.Quantity = 10 - 5 = 5thread2: _product.Quantity = 10 - 1 = 9thread1:_database.Save(); //數量為5thread2:_database.Save(); //數量為9
剛剛發生了什么?我們已售出 6 臺筆記本電腦,但僅從庫存中減少了一臺。
如何解決這個問題?
為了確保只有正數,我們可以使用一些 DB 約束(模仿 unsigned int)。
為了處理競爭條件,我們通常使用lock和類似的技術。根據可能有效的情況,如果我們有一個服務器實體......但是,當我們有多個服務器實體并且服務器在多執行緒環境中運行時,我們應該怎么做?
It seems to me that the moment you have more than one web server, your only reasonable option for locking is the database. Why do I say reasonable? Because we have Mutex.
A lock allows only one thread to enter the part that's locked and the lock is not shared with any other processes.
A mutex is the same as a lock but it can be system-wide (shared by multiple processes).
Now...This is my personal opinion, but I expect that managing Mutex between a few processes in microservice-oriented world where a new instance of the server can spin up each second or where the existing instance of the server can die each second is tricky and messy (Do we have some Github example?).
How to solve the problem then?
- Stored procedure* - offload the responsibility to the database. Write a new stored procedure and wrap the whole logic into a transaction. Each of the servers will call this SP and we don't need to worry about anything. But this might be slow?
- SELECT ...FOR UPDATE - I saw this while I was investigating the problem. With this approach, we still try to solve the problem on 'database' level.
Taking into account all of the above, what should be the best approach to solve this problem? Is there any other solution I am missing? What would you suggest?
I am working in .NET and using EF Core with PostgreSQL, but I think that this is really a language-agnostic question and that principle for solving the issue is similar in all environments (and similar for many relational databases).
uj5u.com熱心網友回復:
在閱讀了大部分評論后,讓我們假設您需要一個關系資料庫的解決方案。
您需要保證的主要事情是代碼末尾的寫操作僅在前提條件仍然有效時才發生(例如product.Quantity - requestedQuantity)。
這個先決條件在應用程式端在記憶體中進行評估。但是,當發生資料庫讀取時,應用程式目前只能看到資料的快照:_database.GetProduct();一旦其他人正在更新相同的資料,這可能會過時。如果您想避免SERIALIZABLE用作事務隔離級別(無論如何都會影響性能),應用程式應該在寫入時檢測前提條件是否仍然有效。或者換一種說法,如果資料在處理它時沒有改變。
這可以通過使用離線并發模式來完成:樂觀離線鎖或悲觀離線鎖。許多 ORM 框架默認支持這些功能。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/359328.html
標籤:database multithreading oop race-condition system-design
上一篇:異步執行緒在TPL等待時死亡
