事务的四大特性(ACID)
- 原子性(Atomicity)
- 一致性(Consistency)
- 隔离性(Isolation)
- 持久性(Durability)
原子性:事务内执行的sql操做看成一个总体,要么同时成功,要么同时失败回滚 一致性:强调的是数据的状态,指的是数据库从一个一致性状态到另一个一致性状态,打个比方,A有200,B有100,A转100给B,那么最后的一致性状态是A有100,B有200,若是状态是A有100,B仍是原来的100,那这就不符合数据的一致性 隔离性:一个事务所作的修改在最终提交之前,对其余事务是不可见的,也就是说,A有200,B有100,A转100,B加100,尚未提交的时候,又开启了一个事物,此时看到的是A有200,B有100,也就是第一个事物没有commit以前的状态;还有一点就是说事物对某 行的某个字段进行加操做,那么在没有提交以前,其余事物是不能修改这一行的这个字段的,由于mysql此时的行锁被第一个事物拿到,commit以后才会释放 持久性:事物一旦进行了提交,那么数据落盘,所作的修改会永久保存到数据库mysql
事务的隔离级别
事务的隔离级别是为了解决mysql并发问题的几种控制手段。sql
事务的并发问题
一、脏读:事务A读取了事务B更新的数据,而后B回滚操做,那么A读取到的数据是脏数据 二、不可重复读:事务 A 屡次读取同一数据,事务 B 在事务A屡次读取的过程当中,对数据做了更新并提交,致使事务A屡次读取同一数据时,结果 不一致。 三、幻读:系统管理员A将数据库中全部学生的成绩从具体分数改成ABCDE等级,可是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉同样,这就叫幻读。 小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住知足条件的行,解决幻读须要锁表 <font color='red'>单条</font>修改数据的命令会自动的触发事务,包括insert、update、delete。 而在SQL语句中有手动开启事务的缘由是:能够进行屡次数据的修改,若是成功一块儿成功,不然一块儿会滚到以前的数据.数据库
4种隔离级别
Read Uncommitted(读取未提交内容)
在该隔离级别,全部事务均可以看到其余未提交事务的执行结果。本隔离级别不多用于实际应用,由于它的性能也不比其余级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。session
Read Committed(读取提交内容)
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它知足了隔离的简单定义:一个事务只能看见已经提交事务所作的改变。,由于同一事务的其余实例在该实例处理其间可能会有新的commit,因此同一select可能返回不一样结果,这就是不可重复读并发
Repeatable Read(可重读)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到一样的数据行。不过理论上,这会致使另外一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另外一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎经过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。性能
Serializable(可串行化)
这是最高的隔离级别,它经过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每一个读的数据行上加上共享锁。在这个级别,可能致使大量的超时现象和锁竞争。测试
在测试的时候,把mysql事务级别设置成 Repeatable Read,一个事务往里插数据,另外一个事务是看不到其余事务插入的数据的,即读的不是最新数据,咱们把这种现象也看做是幻读fetch
查看和设置隔离级别
SELECT @@tx_isolation
设置mysql的隔离级别:set session transaction isolation level 设置事务隔离级别
spa
pymysql
对于pymysql有几个点须要注意线程
默认支持事务
pymysql默认开启事务,从每次执行执行增删改都须要最终commit就能知道。 经过pymysql增长数据但未提交时候,发现表的
AUTO_INCREMENT
是一直递增的(说明数据库已经收到插入指令了,就等着拍板呢),这是为了解决并发的考量,而且若是回滚AUTO_INCREMENT
并不会减小,而是一直递增下去,这样才不会出错。
cursor对象存放查询的数据
import pymysql conn = pymysql.connect(host='localhost', user='root', password="123456",database='test', port=3306, charset='utf8') cur = conn.cursor() cur.execute('select * from department') Out[5]: 4 cur.fetchone() Out[6]: (200, '技术') cur.execute('select * from employee') Out[7]: 18 cur.fetchone() Out[8]: (1, 'egon', 'male', 18, datetime.date(2017, 3, 1), '办事处外交大使', None, 7300.33, 401, 1)
经过上面咱们能够发现,当咱们查询的时候,取数据没有取完就执行另外一条查询语句,那么先前的查询结果会被覆盖。也就是说执行了一条查询语句,那么就应该当即取出结果,不然拿着这个cursor去执行任何增删改查操做,原来存储的查询数据都会被清掉
pymysql模块的threadsafety = 1
若是在多个线程中的cursor共用一个conn链接,那么就会报相似pymysql.err.InternalError: Packet sequence number wrong - got 45 expected 0
的错误
- 每一个线程拥有本身的链接
- 全部线程共用一个链接池,须要考虑线程总数和链接池链接数上限的问题
pymysql遇到的幻读
以前我开了两个线程,一个线程取数据,一个线程往表插入数据,最终的结果是每次取得的数据都是同样的,可是数据表确确实实是在不停地增长数据。这是由于数据库的隔离级别形成的.解决办法有两个:
- 修改隔离级别
- 创建conn的时候加上
autocommit=True
参数