事务的性质和隔离级别
事务的三个基本要素:ACID
事务:是一系列的数据库操作,是数据库应用的基本逻辑单位
- 原子性:即不可分割性,事务要么全部被执行,要么就全部不被执行。
- 一致性或可串性:事务的执行使得数据库从一种正确状态转换成另一种正确状态。
- 隔离性:在事务正确提交之前,不允许把该事务对数据的任何改变提供给任何其他事务。
- 持久性:事务正确提交后,其结果将永久保存在数据库中,即使在事务提交后有了其他故障,事务的处理结果也会得到保存。
事务并发问题
事务并发时出现的场景:
- 更新丢失:事务A将数值5改为1并提交; 事务B将数值5改为2并提交。 这时数据的值为2,事务A所做的更新将会丢失。或者事务B中途失败退出,导致对数据的两个修改都失效了。数值还是原来的5
- 脏读:一个事务读到另一个事务,尚未提交的修改,就是脏读。这里所谓的修改,除了Update操作,还包括Insert和Delete操作。脏读的后果:如果后一个事务回滚,那么它所做的修改,统统都会被撤销。前一个事务读到的数据,就是垃圾数据。
事务1:在Reservation表中插入一条记录,用于预订99号房间。
事务2:查询,尚未预定的房间列表,因为99号房间,已经被事务1预订。所以不在列表中。
事务1:信用卡付款。由于付款失败,导致整个事务回滚。所以插入到Reservation 表中的记录并不置为持久(即它将被删除)。现在99号房间则为可用。所以,事务2所用的是一个无效的房间列表,因为99号房间,已经可用。 不可重复读:在同一个事务中,再次读取数据时【就是你的select操作】,所读取的数据,和第1次读取的数据,不一样了。就是不可重复读。
事务1:查询所有的年龄小于15的学生。
事务2:修改一条年龄小于15的学生为年龄大于15。
事务1再次执行,得到俩次不一样的查询结果。在不可重复读里面,可以看到其他事务所做的修改,而导致2次的查询结果不再一样了。这里的修改,是提交过的。也可以是没有提交的,这种情况同时也是脏读。幻读:事务1读取指定的where子句所返回的一些行。然后,事务2插入一个新行,这个新行也满足事务1使用的查询where子句。然后事务1再次使用相同的查询读取行,但是现在它看到了事务2刚插入的行。这个行被称为幻象,因为对事务1来说,这一行的出现是不可思议的。
不可重复读的重点是修改
同样的条件 , 你读取过的数据 , 再次读取出来发现值不一样了
幻读的重点在于新增或者删除
同样的条件 , 第 1 次和第 2 次读出来的记录数不一样
SQL定义的四个事务隔离级别
- 未授权读取(Read Uncommitted)(读未提交):允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
- 授权读取(Read Committed)(读已提交):允许不可重复读取和幻读,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。当前事务只能读取到其他事务已经提交后的数据。最为常用。
- 可重复读取(Repeatable Read):保证当前事务多次读取特定记录的结果相同,可以防止脏读、不可重复读,但可能会幻读。
- 序列化(Serializable):提供严格的事务隔离。它要求事务序列化执行,事务只能放在队列里一个接着一个地执行,但不能并发执行。一个时刻只能一个事务操作数据库。降低了系统的并发能力。
大多数数据库的默认隔离级别为: Read Commited,如Sql Server , Oracle。
少数数据库默认的隔离级别为Repeatable Read, 如MySQL InnoDB存储引擎。
MyISAM引擎使用的是表锁。对于MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但是会阻塞对同一表的写请求;对MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作。MyISAM的读和写之间,以及写操作之间都是串行的
InnoDB引擎与MyISAM的最大不同点有两处:一是支持事务,二是采用行级锁(也可以用表锁,默认行级锁),
共享锁:允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁;
排他锁:允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。
默认 对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁;对于普通SELECT语句,InnoDB不会加任意锁。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁;对于普通SELECT语句,InnoDB不会加任意锁。
在InnoDB中,锁是逐步获得的,因此发生死锁是可能的。发生死锁后,InnoDB一般都能自动检测到,并使一个事务释放锁并回退,另外一个事务获得锁,并继续完成事务。但在涉及外部锁,或涉及表锁的情况下,InnoDB并不能完全自动检测到死锁,这需要通过设置锁等待超时参数innodb_lock_wait_timeout来解决。
关于锁机制,未完待续