多线程下两个MySQL连接数据操作后无法同步更新的问题排查
问题分析与解决方案
核心原因
你的问题根源是MySQL事务隔离机制+未正确管理事务生命周期:
MySQL默认采用REPEATABLE READ(可重复读)隔离级别,在这个级别下,一个事务启动后会读取事务开始时的数据快照,后续其他连接提交的更新不会被当前事务感知,直到当前事务提交或回滚。而mysql.connector默认关闭了自动提交(autocommit=False),如果连接1执行SELECT后没有主动提交事务,就会一直停留在旧的数据快照中,看不到连接2的更新。
另外,你当前的代码存在严重的SQL注入漏洞,必须优先修复。
解决步骤
1. 开启自动提交或手动管理事务
方案一:全局开启自动提交
修改Database类的构造函数,在连接建立后开启自动提交,这样每个SQL语句执行后都会自动提交事务,不会保留旧快照:
class Database(): def __init__(self): self.mydb = mysql.connector.connect( host=config.botConfig["host"], user=config.botConfig["user"], password=config.botConfig["password"], port=config.botConfig["port"], database=config.botConfig["database"], charset='utf8mb4' ) self.mydb.autocommit = True # 开启自动提交 self.cursor = self.mydb.cursor() if self.mydb.is_connected(): print("Database connection successful") else: print("Database connection failed")
方案二:手动提交事务
如果不想全局开启自动提交,确保连接1每次查询完成后提交事务,刷新数据快照:
# 连接1的查询示例 def get_world_data(self, idWorld): sql = "SELECT * FROM worlds_has_blocks WHERE idWorld = %s" self.cursor.execute(sql, (idWorld,)) result = self.cursor.fetchall() self.mydb.commit() # 提交事务,释放旧快照 return result
2. 修复SQL注入漏洞
绝对不能用字符串拼接的方式构造SQL语句,改用参数化查询(mysql.connector用%s作为占位符):
def add_block_to_world(self, idWorld, idBlock, x, y): # 查询是否存在 sql = "SELECT idRel FROM worlds_has_blocks WHERE idWorld = %s AND x = %s AND y = %s;" self.cursor.execute(sql, (idWorld, x, y)) res = self.cursor.fetchone() if res: # 更新块 sql = "UPDATE worlds_has_blocks SET idBlock = %s WHERE idWorld = %s AND x = %s AND y = %s;" self.cursor.execute(sql, (idBlock, idWorld, x, y)) else: # 插入新块(注意VALUES是复数,你之前的拼写有误) sql = "INSERT INTO worlds_has_blocks VALUES(NULL, %s, %s, %s, %s);" self.cursor.execute(sql, (idWorld, idBlock, x, y)) self.mydb.commit()
3. 多线程连接的正确性说明
你每个线程使用独立Database对象的做法是正确的——MySQL的连接对象不是线程安全的,跨线程共享单个连接会导致各种异常(比如你之前遇到的报错),所以每个线程维护自己的连接是标准的正确做法。
内容的提问来源于stack exchange,提问作者JanikCodes
相关产品推荐
相关产品推荐

