0 bug初现
业务当中有这么一条业务代码:
从数据库中查询用户A相关的记录,如果没有就创建一条。
测试阶段都没有遇到什么问题,然后正在上线后发现,会出现一种情况:用户存在两条数据
1 初步判断
初步推断原因为:网络阻塞情况下,用户点了下按钮没有反映,然后又刷新页面又自动发了一次请求,等网络不阻塞了,两个请求同时到了服务器,然后同时发现数据库中都没有这条记录,于是同时创建了一条。就产生了上面的现象。
2 bug复现模拟
使用脚本模拟十次并发,直接调用 相关函数。
- 模拟这里碰到了问题,因为flask的函数环境都是默认在单线程中完成的。
当我们直接因为flask app 里面的特定函数并且去执行的时候,肯定是out_of_app_context报错的。 - 还是要模拟线上环境,由gunicorn来当服务器托起app,然后使用多进程脚本来模拟并发。
import requests
url = "http://localhost:8000/api/v1/test"
payload = "{}"
headers = {
'content-type': "application/json",
'cache-control': "no-cache",
'postman-token': "665a76a5-f37f-96d9-cdbd-9c7f66f1f6ba"
}
def r ():
response = requests.request("POST", url, data=payload, headers=headers)
print(response.text)
import threading
for i in range(10):
t = threading.Thread(target=r)
t.start()
request相关的代码可以直接复制postman里面的code
运行脚本,果然复现出了bug
File "/home/ubuntu/.virtualenvs/coucou/local/lib/python3.5/site-packages/sqlalchemy/orm/query.py", line 2827, in one_or_none
"Multiple rows were found for one_or_none()")
sqlalchemy.orm.exc.MultipleResultsFound: Multiple rows were found for one_or_none()
3 解决方式分析
- 1采用orm的first来筛选,但是没有解决掉数据库数据重复的问题
- 2 代码层面限于flask线程环境,无法进行锁的操作,也就无法在代码层面解决bug。(非锁的问题,之前的思路想错了)
- 3 考虑使用mysql数据库锁,因为之前从未实践过mysql的锁,所以以下开始学习。
- 4 采用unique索引,从数据库层面去解决数据重复的问题。(这才是正确答案)
4 mysql锁初识
- 参看数据库有用到锁
show lock tables - pass










网友评论