问题描述
有两个不同的守护进程,一个守护进程接收csi数据插入到数据库中,另一个守护进程从数据库中读取csi数据进行处理。
在两个守护程序运行的过程中,从数据库中读取csi数据进行处理的守护进程在数据库中有新数据的情况下无法读取到数据库的新数据。经过排查发现,新数据确实被接收csi数据的守护进程成功插入,但是读取csi数据的守护进程并没有从数据库中读取到新数据,因此排除了数据库的问题和接收csi数据的守护进程的问题,将目光放在读取csi数据的守护进程上。
问题解决
经过一段时间的排查发现,很可能是读取csi数据的守护进程涉及到了 MySQL 的事务隔离级别和查询缓存的影响。
- 事务隔离导致看不到新数据(设置正确的事务隔离级别:
READ COMMITTED
,可以看到其他事务的提交) - 查询缓存导致总是返回旧数据(禁用缓存,每次都读取最新数据)
为了保险起见,还防止有以下问题:
- 连接断开后无法自动重连(自动重连,使连接保持有效)
- 事务未正确提交导致数据更新不及时(显式提交,确保事务正确提交)
一些疑问
在进行web程序的编写过程中,我从来没有遇到过因为事务隔离级别引发的问题。为什么spring不需要设置正确的事务隔离级别,可以读取新的数据呢?
可能有以下几个原因:
- Spring 默认的事务隔离级别就是 READ_COMMITTED
- Spring 的事务管理机制会自动处理事务的开启和提交
- Spring 使用连接池(如 HikariCP)管理数据库连接
- Spring JPA/MyBatis 等 ORM 框架会自动清除查询缓存
实际上 Spring 在底层都处理好了这些细节。而在 Python 中,我们需要自己处理这些数据库事务和连接的细节。
示例代码:
def _fetch_csi_data(self, limit=30):
try:
# 1. 手动设置事务隔离级别
cursor.execute("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED")
# 2. 手动管理事务
self.db_connection.commit() # 开启新事务
# 3. 手动禁用查询缓存
query = """
SELECT /*+ NO_CACHE */ id, timestamp, csi_data
FROM raw_data
WHERE device_id = %s AND processed = 0
ORDER BY timestamp DESC
LIMIT %s
"""
# 4. 手动提交事务
self.db_connection.commit()
except Error as e:
# 5. 手动处理异常
self.logger.error(f"获取 CSI 数据时发生错误: {e}")
self.close()
return []