一般来说,事务是必须满足4个条件(ACID)::原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。
1、用 BEGIN, ROLLBACK, COMMIT来实现
2、直接用 SET 来改变 MySQL 的自动提交模式:
通过CAS机制实现(Compare and Swap)
CAS可以理解为CAS(V,O,N)V当前内存实际存放的值
O预期值(旧值)
N更新的新值
通过一直去比较如果当前存放的值和预期的值一样,那么进行更新。
但这样存在一个问题,ABA问题
事务1想要将数据A改成B
事务2把数据A改成C
事务3把数据C改为A
此时事务1CAS成功,将数据A改为B
这就是ABA问题,数据被改又改回来可能引发程序错误
那么怎么改进呢,解决办法可以给数据库中增加一个version字段
或者增加一个字段使用时间戳timestamp。
这样在更新的时候检查数据的时间戳和自己之前的对比,如果一致就ODF,否则就是版本冲突。
乐观锁相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现
优点:乐观锁机制避免了长事务中数据库的开销增大,大大提高并发量
意向锁
1在MySQL中有表锁,读锁锁表,会阻塞其他事务修改数据。写锁锁表,会阻塞其他事务读写。
2innerdb引擎支持行锁,行锁分也为共享锁,一个事务对一行的共享只读锁。排它锁,一个事务对一行的排它读写锁。
3这两种类型的锁共享的问题:事务A锁住一行,让一行只读,事务B申请整个表的写锁。如果B申请成功,那么理论上它能修改表中任意一行,这与A持有的行锁是冲突的。数据库避免这个冲突,就是让B申请被阻塞,直到A释放了行锁。
那么B怎么去判断这个冲突呢?
step2的效率不高,因为要遍历每一行,于是就有了意向锁,上述判断改为
申请意向锁的动作是数据库完成的,就是说,事务A申请一行的行锁时候,数据库会自动申请表的意向锁,不需要代码去申请。
共享锁
加锁与解锁:当一个事务执行select语句时候,数据库会为这个事务分配一把共享锁,来锁定被查的数据。在默认情况下,数据被读取后,数据库会立即解除共享锁。。例如,当一个事务执行查询“SELECT * FROM accounts”语句时,数据库系统首先锁定第一行,读取之后,解除对第一行的锁定,然后锁定第二行。这样,在一个事务读操作过程中,允许其他事务同时更新accounts表中未锁定的行。
兼容性:如果数据资源上放置了共享锁,还可以再放置共享锁或者更新锁。
并发性:具有良好的并发性
排他锁
加锁与解锁:当一个事务执行insert,update,delete语句时候,数据库会自动对SQL语句操作的数据资源使用独占锁。如果该数据资源已经有其他任何锁存在,就无法再放置独占锁了。
兼容性: 独占锁不能和其他锁兼容,如果数据资源上已经加了独占锁,就不能再放置其他的锁了。同样,如果数据资源上已经放置了其他锁,那么也就不能再放置独占锁了。
并发性:最差。只允许一个事务访问锁定的数据,如果其他事务也需要访问该数据,就必须等待。
更新锁
更新锁在初始化阶段用来锁定可能被修改的资源,这可以避免使用共享锁造成死锁的现象。
例如:UPDATE person SET age=18 WHERE id=1;
更新操作需要两步:读取person表中id为1的记录,然后执行更新操作
更新锁以下特点:
1加锁与解锁,当一个事务执行update语句时,数据库会先为事务分配一把更新锁,当数据读取完毕,执行跟新操作时,把更新所升级为独占锁
2兼容性:更新锁与共享锁兼容的,也就是说,一个资源可以同时放置跟新锁和共享锁,但最多只能放置一把更新锁。这样,多个事务跟新相同数据时,只有一个事务可以获取更新锁,然后把更新锁升级为独占锁,其他事务必须等到前一个事务结束后,才能获取更细锁,这就避免了死锁。
数据库的隔离级别
了解了数据库的锁机制,理解数据库的隔离级别也就好多了,每一种隔离级别满足不同的数据需求,使用不同程度的锁。
读未提交:读写均不使用锁,诗句一致性最差,也会出现许多逻辑错误。
读已提交: 使用写锁,但是读会出现不一致,不可重复读。
可重复读:使用读写锁,解决了不可重复读的问题,但会有幻读。
可串行化:使用事务串行化调度,避免出现因为插入数据没法加锁导致的不一致情况
读未提交,造成脏读
一个事务中 的读操作,可能读到聆听事务中未提交修改的数据,如果事务发生回滚可能造成错误。
例子:A打100给B,B看账户,这是两个操作,针对同一个数据库,两个事务,如果B读到了A事务的100块,认为钱打过来了但是A事务回滚了,造成损失。
避免这些事情的发生就需要我们在写操作的时候加锁,使读写分离,保证读数据的时候,数据不被修改,写数据的时候,数据不被读取。从而保证写的同时不能被另个事务写和读。
读提交(Read Committed)
加了写锁,就保证不出现脏读,也就是保证读的都是提交之后的数据,但是会造成不可重读读,读的时候不加锁,一个读的事务过程中,如果读取两次,在两次之间有写事务修改了数据,将导致两次读取结果不一致,从而导致逻辑错误。
可重复读(Repeatable Read)
这就牵涉到事务中是否加读锁,并且读操作加锁后是否在事务commit之前持有锁的问题,如果不加读锁,必然出现不可重复读,如果加锁读完立即释放,不持有,那么就可能在其他事务中被修改,若其他事务已经执行完成,此时该事务中再次读取就会出现不可重复读
所以读锁在事务中持有保证不出现不可重复读,写的时候必须加锁且持有,可重复读是MySQL默认的事务隔离级别,上面的意思是读的时候加锁并且保持
可串行化(Serializable)
解决幻读问题,同一个查询多次返回的结果不一致,事务A增加一条记录,事务B在事务A提交前后各执行了一次查询操作,发现前后多了一条数据,幻读是由于并发事务增加记录导致的,这个不能像可重复读一样加锁解决,因此对于增加的记录无法加锁,只能将事务串行化,才能避免幻读。
这是最高级别的,他通过强制将事务串行化,使其不可能相互冲突,从而解决幻读问题,简言之,就是在每个读的数据上加上共享锁,这个级别,可能导致大量的超时现象和锁竞争
使用实例
MySQL中的隔离级别和悲观锁以及乐观锁实例
MySQL的事务支持
MySQL的事务不是绑定在MySQL服务器本身上,而是与存储引擎相关
1MyISAM:不支持事务,用只于读程序提高性能
2InnoDB:支持ACID事务,行级锁,并发
3Berkeley BD :支持事务
文章参考:http://www.runoob.com/mysql/mysql-transaction.html
https://blog.csdn.net/C_J33/article/details/79487941