一、发现问题
近期部署一个内部服务站点,提供一个web界面供查询和展示分析后的数据报表,启动时一段时间内数据查询获取的都是最新值,等待几个小时后,网页查询出来的结果不是最新值,但是数据库中是存在更新的数据记录,不知何故?
二、排查过程
在MySQL中事务的隔离级别有4种类型:
- 读未提交(READ UNCOMMITTED)
- 读已提交(READ COMMITTED)
- 可重复读(REPEATABLE READ)
- 序列化(SERIALIZABLE)
MySQL 默认的事务隔离级别是可重复读(REPEATABLE READ),这 4 种隔离级别的说明如下。
1.READ UNCOMMITTED
读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,而未提交的数据可能会发生回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。
2.READ COMMITTED
读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据,因此它不会有脏读问题。但由于在事务的执行中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。
3.REPEATABLE READ
可重复读,是 MySQL 的默认事务隔离级别,它能确保同一事务多次查询的结果一致。但也会有新的问题,比如此级别的事务正在执行时,另一个事务成功的插入了某条数据,但因为它每次查询的结果都是一样的,所以会导致查询不到这条数据,自己重复插入时又失败(因为唯一约束的原因)。明明在事务中查询不到这条信息,但自己就是插入不进去,这就叫幻读 (Phantom Read)。
4.SERIALIZABLE
序列化,事务最高隔离级别,它会强制事务排序,使之不会发生冲突,从而解决了脏读、不可重复读和幻读问题,但因为执行效率低,所以真正使用的场景并不多。
因为默认MySQL设置的是REPEATABLE READ,所以存在重复读的情况,为解决此问题,需要采用commit(),原因在于:
MySQL如果直接select,默认是自动commit,但是采用pymysql是默认开启事务(需要手动commit),为了每次都能读取到最新的更新,需要commit,如果需要在事务内重复度,则不需要commit,这与隔离级别有关。
三、解决方案
try:
cursor.execute("select u_time,ip from u_data where datediff(now(),u_time)<1 order by u_time desc;")
conn.commit()
except:
conn.rollback()
标签:事务,隔离,READ,级别,查询,提交,Mysql,数据
From: https://blog.51cto.com/ciscoskys/8561393