事务(Transaction)
在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
- 为什么要有事务呢:
为了确保逻辑的成功。例:银行的转账。
一、事务的执行过程
- 命令行演示事务的执行
命令行一般默认事务自动提交,所以需要设置事务手动提交。set autocommit=off;
开始事务:start transaction;
结束事务:
commit;
:提交事务,数据将会写到磁盘上的数据库中rollback;
:回滚,回到数据最初的状态。
- Jdbc演示事务的执行
connection.setAutoCommit(false);//设置事务手动提交
connection.commit();//提交事务
connection.rollback();//回滚,回归数据最初的状态。
二、事务特性(ACID)
- 原子性(Atomicity):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
- 一致性(Consistency):在事务开始之前和事务结束之后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度,串联性以及后续数据库可以自发性地完成预定的工作。
- 隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted),读已提交(read committed),可重复读(repeatable read),串行化(Serializable)。
- 持久性(Durability):事务处理结束后,对数据的修改就是永久,即便系统故障也不会丢失。
三、事务安全隐患
读数据时会出现的安全隐患:
- 隔离级别为读未提交时,会出现脏读情况,指一个事务可以读到另一个未提交前的数据。
- 隔离级别为读已提交时,能屏蔽脏读的现象,但是引发了另一个问题,不可重复读。
- 隔离级别为可重复读时,可以重复读取数据。但是会出现幻读,一个事务读到了另一个事务已提交的插入的数据,导致多次查询结果不一致。
- 隔离级别为可串行化时,能够解决前面三个问题,如果有一个连接的隔离级别设置为了串行化,那么谁先打开了事务,谁就有了先执行的权利,谁后打开事务,谁就只能等着,等前面的那个事务,提交或者回滚后,才能执行。这种隔离界别比较少用,容易造成性能上的问题,效率比较低。
更新数据时会出现的安全隐患:
丢失更新,在操作数据库的时候,可能会由于并发问题而引起的数据的不一致性(数据冲突),解决办法:
- 乐观锁
乐观锁不是数据库自带的,需要自己实现。乐观锁是指操作数据库时(update
操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊操作(不加锁),而是在进行更新后,再去判断是否有冲突。
实现方法:给数据表加一个版本(version)字段,每操作一次,将将那条记录的版本号加1。也就是先查询出那条记录,获取出version字段,如果要对那条记录进行操作(更新),则先判断此刻version的值是否与刚刚查询出来时的version的值相等,如果相等,则说明这段期间,没有其他程序对其进行操作,则可以执行更新,将version字段的值加1;如果更新时发现此刻的version值与刚刚获取出来的version的值不相等,则说明这段期间已经有其他程序对其进行操作了,则不进行更新操作。 - 悲观锁
悲观锁就是操作数据时,已经认为此操作会出现冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作,所以悲观锁需要耗费较多的时间,悲观锁是由数据库实现的。要用的时候调用相关语句:
select * from bank for update;