悲观锁怎么写?拿不到锁时有哪几种处理?用它做任务队列安全吗?死锁是怎么发生的、怎么避免?这篇在真实的 PostgreSQL 上用多线程把每种情况都跑一遍。
SELECT counter.id, counter.n FROM counter WHERE counter.id = ... FOR UPDATE read=True → FOR SHARE nowait=True → FOR UPDATE NOWAIT skip_locked=True → FOR UPDATE SKIP LOCKED
上一篇:乐观锁;下一篇:SQLAlchemy 查询日志。
期望 160,最终 n = 160,耗时 359 ms(没有重试,但线程在排队)
nowait=True → LockNotAvailable,用时 1 ms skip_locked=True → 返回空(被跳过),用时 1 ms lock_timeout='500ms' → LockNotAvailable,用时 505 ms 默认(阻塞等待) → 拿到了,用时 1011 ms(等持有者 1 秒后 commit)
每个 worker 拿到的任务: {0: [2, 5, 8], 1: [3, 6, 9], 2: [1, 4, 7]} | 全部 [1, 2, 3, 4, 5, 6, 7, 8, 9] | 有没有重复: False | 184 ms 相反顺序: {'B': ('DeadlockDetected', '1004 ms'), 'A': ('成功', '1005 ms')}
相同顺序(都先 1 后 2): {'B': '成功', 'A': '成功'}两个线程各自的 Session,中间 sleep 0.3 秒保证两边都拿到了第一把锁。