跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • 关于 Django 的乐观锁问题
未分類
11 11 月 2020

关于 Django 的乐观锁问题

关于 Django 的乐观锁问题

資深大佬 : IurNusRay 2

Django 的乐观锁真的有作用吗?我写了个例子,用 jmeter 开启 1000 个线程去请求,结果并不能解决资源竞争的问题,代码如下:
with transaction.atomic():
save_id = transaction.savepoint()
try:
book = BookInfo.objects.get(id=1)
origin_read = book.read
new_read = origin_read + 1
BookInfo.objects.filter(id=book.id, read=origin_read).update(read=new_read)
book.save()
except Exception as e:
print(e)
transaction.savepoint_rollback(save_id)

transaction.savepoint_commit(save_id)

理论上,book 表的 read 资源在 1000 次请求之后应该会从 0 增加到 1000,然而实际上只有 500 左右,当我降低线程数量到 100,也只能到 78,这是为什么呢

大佬有話說 (13)

  • 資深大佬 : nonduality

    你在 try 里头加一行输出 log,看执行情况,也许是有些请求 miss 了(用内置开发服务器的话可能性很大)。

    此外,你可以用 F 表达式,按说是可以避免 race condition 问题:

    “`python
    try:
    book = BookInfo.objects.get(id=1)
    BookInfo.objects.filter(id=book.id, read=origin_read).update(read=F(‘read’)+1)
    book.save()
    except Exception as e:
    ….

    “`

  • 資深大佬 : nonduality

    try 里头改成这两行差不多就行
    book = BookInfo.objects.get(id=1)
    BookInfo.objects.filter(id=book.pk).update(read=F(‘read’)+1)
    使用 F 表达式后,不确定还需不需要使用 transaction,你可以测试下

  • 主 資深大佬 : IurNusRay

    @nonduality F 表达式我也试过,也不行

  • 主 資深大佬 : IurNusRay

    之前是用 runserver 运行的,可能并发支持不行,刚刚换成 uwsgi 运行,发现结果如下:
    1. book.read += 1 这种方式无法解决资源竞争,实测 1000 次请求,只能加到 500 左右
    2. book.read = F(“read”) + 1 这种方式可以解决,实测 1000 次并发请求,分 5 批,最后 read 值加到了 5000
    3. book = BookInfo.objects.get(id=1)
    origin_read = book.read
    BookInfo.objects.filter(id=book.id, read=origin_read).update(read=origin_read + 1)
    这种所谓“乐观锁“的方式,实测完全无效,1000 次请求,read 值只能加到 500 左右

    综上,使用 F 表达式是最有效的方式,不是很明白这种乐观锁的作用是什么,既没有解决资源竞争,实际运行也没有任何报错

  • 資深大佬 : nonduality

    @IurNusRay 我刚测试了一下,用 F 表达式进行数据自加,gunicorn 起 1 个进程跑,用 ab -n 1000 -c 100 测试,完全没问题。所以,用 F 表达式对多数情形下是够用的。

  • 資深大佬 : nonduality

    我不太清楚谁提出来“乐观锁”,但看其实现,大概是要保证 filter 到的实例状态具备 origin_read 的值,在此基础上 update 数值,但就算有 trasaction,也无法保证它一定获取到你要的数据状态,自然就 miss 掉了。但 F 表达式不一样,它用 SQL 语句在数据库层面直接操作数据的。

  • 主 資深大佬 : IurNusRay

    @nonduality 刚刚有看了一下,原来是我代码漏掉了一部分,这个”乐观锁”的原理是要开启一个循环,在成功+1 的时候退出循环,否则继续, 比如 row = BookInfo.objects.filter(id=book.id, read=origin_read).update(read=origin_read + 1),当 row 为 0 时继续循环。

    但是经过测试,仍然达不到 F 表达式的效果,1000 次请求只能加到 990 左右,所以,还是用 F 表达式吧

  • 資深大佬 : todd7zhang

    @IurNusRay 我试过,django3.1, 默认使用 sqlite3 数据库,不开启事务的时候。1000 次,20 并发 sleep 重试是能实现的

  • 資深大佬 : todd7zhang

    https://paste.ubuntu.com/p/hJVKCFmKtq/

  • 資深大佬 : todd7zhang

    如果在外面包一个 atomic, 在执行 filter().update 时,会触发 sqlite3.OperationalError: database is locked 。然后尝试对 save_point rollback 时又有新的 An error occurred in the current transaction. You can’t execute queries until the end of the ‘atomic’ block 。不知道是不是 sqlite 的 isolation level 导致的,我后面试一下 postgreSQL 。 按理来说,只要是 read committed 就可以的

  • 資深大佬 : todd7zhang

    实测 postgreSQL 没毛病, 1000 个请求,20 并发。https://paste.ubuntu.com/p/pxdPM9B8Cx/

    所有的服务都是直接 runserver

  • 資深大佬 : nonduality

    @todd7zhang 我用 F 表达式,不用 transaction,1000 请求,100 并发(尝试过更高,但受系统限制开不起来),多次测试都完全正常。如果不涉及关键的数值,用 F 表达式足够了,用 transaction 降低数据库性能。

  • 資深大佬 : todd7zhang

    @nonduality 这种 filter().update() 主要还是为了防止超售吧,如果你单纯的为了+1, F 表达式确实可以

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具