模拟一个转账业务:
上图中的sql语句:
update from table set money = mongey - 100 where name = 'A';
update from table set money = mongey + 100 where name = 'B';
但是如果将A姓名或转B姓名写错就会出现,A的金额减掉了,但是B未增加;或者是A的金额未减掉了,但是B增加了,这样就造成了数据库中数据不一致;而事务恰好能解决这个问题。
事务(Transaction):就是将一组sql语句放在同一批次内执行,如果出现sql语句执行错误,则该批次内的所有sql都将取消执行。MySQL数据库中仅InnoDB和BDB类型的数据库表支持事务。
事务的特点:一个事务中如果有一个数据库操作失败,那么整个事务的所有数据库操作都会操作失败,数据库数据就会回滚到该事务开始之前的状态。
数据库中的事务执行作为原子粒度,要么都执行成功,要么都不成功,不存在执行部分成功,部分失败的情况。
一致性是指事务在执行开始之前和结束之后,数据库的完整性约束没有被破坏,至于中间的过程就没有强行规定,只强调初始状态和结束状态。
事务的执行是互不干扰的,一个事务的内部操作及使用的数据对其他并发事务是隔离的,并发执行的各个事物之间不能互相干扰;数据库通过加锁来保证事物之间的隔离性。
持久性是指,事务执行成功后,无论是提交还是回滚,对数据库中的数据改变都是持久性的。
步骤一:关闭MySQL的自动提交(MySQL默认开启的是自动提交;1开启;0关闭)
set autocommit = 0;
步骤二:开启一个事务,标记事务的起始点
start transaction;
步骤三:执行一组sql语句
update salary set salary = salary + 500 where Sname = '张三';
update salary set salary = salary - 500 where Sname = '李四';
步骤四或步骤五:向数据库提交事务操作
commit;
步骤四或步骤五:将事务回滚,所有的数据库操作被取消,数据将回到最初的状态。
rollback;
步骤六:开启MySQL自动提交
set autocommit = 1;
这样就解决了上面转账的问题,只有当A和B都执行成功的时候然后再commit,数据才会被保存进数据库中;有错误rollback数据就会回到初始状态。但是注意,回滚或者提交是人主观控制,有sql语句未执行是主观判断可以提交也是可以的;所有都正确执行是也可以选择回滚。
事务的隔离性:为了让不同的事物之间不存在相互干扰,就需要对事务的操作进行隔离,事务的隔离性也就是将操作同一数据的事务相互分离开,让操作之间分开有序的执行。
实现事务隔离的方式:通常数据库都是采用锁的机制,保证事务之间的隔离性。
锁的分类:
事务的并发问题:在事务并发执行的时候,如果不进行事务的隔离,那么就会产生脏读、脏写、重复读、幻读的问题;
脏读: 一个事务读取了另一个事务未提交的数据。
示例: 事务A修改了一行数据,但尚未提交。事务B读取了这行未提交的数据,然后事务A回滚。此时,事务B读取的数据实际上是无效的,是“脏”的。
脏写: 一个事务覆盖了另一个事务已经提交的数据。
示例: 事务A修改了一行数据并提交,然后事务B修改了同一行数据并提交。事务B的提交导致了对事务A的修改的丢失,这是一种“脏写”。
重复读:在一个事务内,两次读取同一行数据,但在两次读取之间有其他事务修改了这行数据。
示例: 事务A读取了一行数据,然后事务B修改并提交了这行数据。接着,事务A再次读取相同的数据,发现数据已经发生了变化。这就是“重复读”。
幻读: 在一个事务内,两次查询返回了不同数量的行,但在两次查询之间有其他事务插入了新的数据。
示例: 事务A查询某个条件下的所有数据,然后事务B插入了符合该条件的新数据。接着,事务A再次查询相同的条件,发现返回的行数发生了变化,即出现了“幻读”。
事务的隔离级别:
每个隔离级别都针对事务并发问题中的一种或几种进行解决,事务级别越高,解决的并发事务问题也就越多,同时也就意味着家的锁就越多,所以性能也会越差。
事务的隔离级别解决的问题:
隔离级别 | 事务读取 | 事务写入 | 解决问题 | 存在问题 |
---|---|---|---|---|
READ_UNCOMMITTED | 不加锁 | 加写锁 | 脏写 | 脏读、不可重复读、幻读 |
READ_COMMITTED | 加读锁(每次select完都会释放读锁) | 加写锁 | 脏读、脏写 | 不可重复读、幻读 |
REPEATABLE_READ | 加读锁(每次select完不会释放锁,而是事务结束后才会释放) | 加写锁 | 脏读、脏写、不可重复读、幻读(mysql的innodb已经解决) | 幻读(mysql的innodb不存在) |
SERIALIZABLE | 不论是读还是写所有的事务串行化执行,一个事务的执行必须等其他事务结束 |