پسیمیستیک لاکینگ (Pessimistic Locking) یا قفلگذاری بدبینانه، هنگام خواندن یا شروع تغییر رکورد قفل دیتابیس میگیرد تا ترنزکشنهای رقیب نتوانند همان رکورد را به شکل متعارض تغییر دهند. این روش معمولا با SELECT ... FOR UPDATE پیادهسازی میشود. برای تعارض نادر و فرم طولانی، آپتیمیستیک لاکینگ میتواند مناسبتر باشد.
نمونه SQL در MySQL و PostgreSQL
START TRANSACTION;
SELECT stock FROM products WHERE id = 42 FOR UPDATE;
UPDATE products SET stock = stock - 1 WHERE id = 42 AND stock > 0;
COMMIT;
نمونه Laravel
DB::transaction(function () use ($id) {
$product = Product::whereKey($id)->lockForUpdate()->firstOrFail();
if ($product->stock < 1) throw new RuntimeException('Out of stock');
$product->decrement('stock');
});
نمونه Python و SQLAlchemy
from sqlalchemy import select
with Session(engine) as session:
with session.begin():
product = session.execute(
select(Product).where(Product.id == 42).with_for_update()
).scalar_one()
if product.stock < 1: raise RuntimeError("Out of stock")
product.stock -= 1
نمونه C# در EF Core با SQL خام
await using var tx = await db.Database.BeginTransactionAsync();
// Provider-specific SQL: FOR UPDATE is supported by PostgreSQL.
var product = await db.Products
.FromSqlRaw("SELECT * FROM products WHERE id = {0} FOR UPDATE", id)
.SingleAsync();
product.Stock -= 1;
await db.SaveChangesAsync();
await tx.CommitAsync();
محدودیت
قفل را هنگام فراخوانی API خارجی یا انتظار انسان نگه ندارید. ترنزکشن طولانی Throughput را کاهش میدهد و احتمال ددلاک را بالا میبرد.
در مقاله مقایسه قفلگذاریها در باورژن روش تصمیمگیری بر اساس Contention و Conflict Window توضیح داده شده است.