一、事务//1.事务的概念:事务是指逻辑上的一组操作,这组操作要么同时完成要么同时不完成。参考转账操作。
update account set money=money-100 where name=‘a’;
update account set money=money+100 where name=‘b’;//2.如果你自己不去控制事务,数据库默认一条sql语句就处在自己单独的事务当中。
//3.也可以使用命令去开启一个事务:
start transaction;--开启事务,这条语句之后的sql语句将处在一个事务当中,这些sql语句并不会立即执行
Commit--提交事务,一旦提交事务,事务中的所有sql语句才会执行。
Rollback--回滚事务,将之前所有的sql取消。
conn.setAutoCommit(false);
conn.commit();
conn.rollback();
conn.setSavePoint();
conn.rollback(sp);//4.事务的特性(ACID)
原子性(Atomicity): 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency):事务前后数据的完整性必须保持一致。在事务执行之前数据库是符合数据完整性约束的,
无论事务是否执行成功,事务结束后的数据库中的数据也应该是符合完整性约束的。
在某一时间点,如果数据库中的所有记录都能保证满足当前数据库中的所有约束,则可以
说当前的数据库是符合数据完整性约束的。
隔离性(Isolation): 事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所
干扰,多个并发事务之间数据要相互隔离。
持久性(Durability): 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数
据库发生故障也不应该对其有任何影响。//5.隔离性
将数据库设计为串行化程的数据库,让一张表在同一时间内只能有一个线程来操作。如果将数据库设计为这样,那数据库的
小率也太低了。所以数据库的设计这没有直接将数据库设计为串行化,而是为数据库提供多个隔离级别选项,使数据库的使
用者可以根据使用情况自己定义到底需要什么样的隔离级别。//不考虑隔离性可能出现的问题:
//脏读:一个事务读取到了另一个事务未提交的数据,这是特别危险的,要尽力防止。
a 1000b1000a:
start transaction;
update set money=money+100 where name=b;
b:
start transaction;
select* from account where name=b;--1100commit;
a:
rollback;
b:start transaction;
select* from account where name=b;--1000
//不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同。(一个事务读取到了另一个事务已经提交的数据--
增加记录、删除记录、修改记录),在某写情况下并不是问题,在另一些情况下就是问题。
a:start transaction;
select 活期账户 from account where name=b;--1000 活期账户:1000select 定期账户 from account where name=b;--1000 定期账户:1000select 固定资产 from account where name=b;--1000 固定资产:1000
------------------------------b:
start transaction;
update set money=0 where name=b;
commit;------------------------------select 活期+定期+固定 from account where name=b; --2000 总资产: 2000
//虚读(幻读)
是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。(一个事务读取到了另一个事务已经提交的数据---增加记录、删除记录),在某写情况下并不是问题,在另一些情况下就是问题。
b1000c2000d3000a:start transaction
select sum(money) from account;---3000 3000
-------------------d:start transaction;
insert into account values(d,3000);
commit;-------------------select count(*)from account;---3 3
3000/3 = 1000 1000
//四个隔离级别:
Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)
Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)不可以避免虚读
Read committed:可避免脏读情况发生(读已提交)
Read uncommitted:最低级别,以上情况均无法保证。(读未提交)
安全性考虑:Serializable>Repeatable read>Read committed>Read uncommitted
数据库效率:Read uncommitted>Read committed>Repeatable read>Serializable
一般情况下,我们会使用Repeatable read、Read committed
mysql数据库默认的数据库隔离级别Repeatable read//mysql中设置数据库的隔离级别语句:
如何查询当前数据库的隔离级别?select @@tx_isolation;
如何设置当前数据库的隔离级别?set [global/session] transaction isolation level ...;
~此种方式设置的隔离级别只对当前连接起作用。
set transaction isolation level read uncommitted;
set session transaction isolation level read uncommitted;~此种方式设置的隔离级别是设置数据库默认的隔离级别
set global transaction isolation level read uncommitted;//8.数据库锁的机制:
共享锁:共享锁可以和共享锁共存,不能和排他锁共存。
排他锁:和任意锁都不共存。
mysql:
在非串行化的模式下,数据库在进行查询时不加任何锁。在进行修改记录时会加排他锁。
在串行话模式下,数据库在进行查询时加共享锁,在进行修改是加排他锁。
串行化是依赖锁来工作的。
死锁:如果两头互相等,mysql会自动帮我们停止一个事务,错误退出,而将另一个执行掉。
更新丢失:多线操作数据,在同一个查询结果的基础上进行修改,在进行提交,后提交的数据会将先提交的数据中的修改覆盖掉,造成更新数据丢失掉。
Serializable本身就能防止更新丢失问题。Serializable效率太低下。//9.更新丢失
如果多个线程操作,基于同一个查询结构对表中的记录进行修改,那么后修改的记录将会覆盖前面修改的记录,前面的修改就丢失
掉了,这就叫做更新丢失。
Serializable可以防止更新丢失问题的发生。其他的三个隔离级别都有可能发生更新丢失问题。
Serializable虽然可以防止更新丢失,但是效率太低,通常数据库不会用这个隔离级别,所以我们需要其他的机制来防止更新丢失://乐观锁和悲观锁不是数据库中真正存在的锁,只是人们在解决更新丢失时的不同的解决方案,体现的是人们看待事务的态度。
//悲观锁:
隔离级别不设置为Serializable,防止效率过低。
在查询时手动加上排他锁。
select* from table lock inshare mode(读锁、共享锁)
select* from table forupdate (写锁、排它锁)
如果数据库中的数据查询比较多而更新比较少的话,悲观锁将会导致效率低下。//乐观锁:
在表中增加一个version字段,在更新数据库记录是将version加一,从而在修改数据时通过检查版本号是否改变判断出当
前更新基于的查询是否已经是过时的版本。
如果数据库中数据的修改比较多,更新失败的次数会比较多,程序需要多次重复执行更新操作。//结论:
如果当前的这个表查询比较多而修改比较少,则应该使用乐观锁。
如果当前的这个表修改比较多而查询比较少,则应该使用悲观锁。
很多情况下,更新丢失根本就不是问题,所以在现实开发中往往不考虑此问题,只有在对数据要求非常严格的情况下要解决更新丢失。