事务的四大特性是事务本身具有的特点。简称ACID。
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
事务前后数据的完整性必须保持一致。
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
隔离性(Isolation)
事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
由于事务的前三大特性数据库可以帮我们保证。需要讨论隔离性;
隔离性(Isolation)
我们知道数据库的隔离性问题本质上就是多线程并发安全性问题。
可以用锁来解决多线成并发安全问题,但是如果用了锁,必然会造成程序的性能大大的下降.对于数据库这种高并发要求的程序来说这是不可接受的.
我们可以具体分析下隔离性产生的细节:
如果两个线程并发修改,必然产生多线程并发安全问题,必须隔离开
如果两个线程并发查询,必然没有问题,不需要隔离
如果一个线程修改,一个线程查询,在不同的应用场景下有可能有问题,有可能没问题。
隔离性可能造成的问题:
1.脏读:
一个事务读取到另一个事务未提交的数据
2.不可重复读:
一个事务多次读取数据库中的同一条记录,多次查询的结果不同(一个事务读取到另一个事务已经提交的数据)
3.虚读(幻读)
有可能出现,有可能不出现:
一个事务多次查询整表数据,多次查询时,由于有其他事务增删数据, 造成的查询结果不同(一个事务读取到另一个事务已经提交的数据)
数据库的隔离级别:
1.查询数据库的隔离级别:select @@tx_isolation;
2.修改数据库的隔离级别:
set [session/global] transaction isolation level xxxxxx;
set global transaction isolation level serializable;
不写默认就是session,修改的是当前客户端和服务器交互时是使用的隔离级别,并不会影响其他客户端的隔离级别
如果写成global,修改的是数据库默认的隔离级别(即新开客户端时,默认的隔离级别),并不会修改当前客户端和已经开启的客户端的隔离级别
数据库中的锁:
1. 共享锁
共享锁和共享锁可以共存,共享锁和排他锁不能共存.在非Serializable隔离级别下做查询不加任何锁,在Serializable隔离级别下做查询加共享锁.
案例演示:打开两个mysql客户端,将隔离级别都设置为Serializable级别,
set session transaction isolation level Serializable;--设置后查询加了共享锁
分别在两个客户端中查询:
start transaction;
select * from account;--都能查询出数据,说明共享锁可以共存。
2. 排他锁
排他锁和共享锁不能共存,排他锁和排他锁也不能共存,在任何隔离级别下做增删改都加排他锁.
3.可能的死锁
mysql可以自动检测到死锁,错误退出一方执行另一方
更新丢失问题:
1. 更新丢失问题的产生
两个并发的事务基于同一个查询结果进行修改,后提交的事务忽略了先提交的事务对数据库的影响,造成了先提交的事务对数据库的影响丢失,这个过程就叫做更新丢失
2.更新丢失解决方案:
将数据库设置为Serializable隔离级别,但是我们一般不会将数据库设置为Serializable。那么在非Serializable下又如何解决更新丢失?可以使用乐观锁、悲观锁。
乐观锁和悲观锁并不是数据库中真实存在的锁,而是两种解决方案的名字。
(1)悲观锁:
在查询时,手动的加排他锁,从而在查询时就排除可能的更新丢失。
select * from users for update;
(2)乐观锁:
在表中设计添加一个版本字段,在进行修改时,要求根据具体版本进行修改,
并将版本字段+1,如果更新失败,说明更新丢失,需要重新进行更新。
两种解决方案各有优缺点,如果查询多修改少,用乐观锁.
如果修改多于查询,用悲观锁。