-
Oracle+MySQL分类的最新文章
-
最新文章
-
目录
-
一、事务的简介
事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。
例如:A-->B转账,对应如下的两条SQL语句。
update account set money = money - 500 where name = 'a';
update account set money = money + 500 where name = 'b'
数据库默认事务是自动提交的,也就是发一条SQL它就执行提交。
二、数据库开启事务命令
如果想多条SQL放在一个事务中执行,则需要执行下面的命令。(以MySQL为例)
开始事务
start transaction;
回滚事务
rollback;
提交事务
commit;
三、MySQL中使用事务
3.1 创建表
create tableaccount( id int primary key auto_increment, name varchar(20), money double );
增加数据
insert into accountvalues(null,'aaa',1000); insert intoaccount values(null,'bbb',1000); insert intoaccount values(null,'ccc',1000);
3.2 MySQL中事务是默认自动提交的,每当执行一条SQL,就会提交一个事务(一个SQL就是一个事务)。Oracle中事务是默认不自动提交的,需要在执行SQL语句后,通过commit命令手动提交事务。
3.3 MySQL管理事务
方式一:开启事务管理SQL的语句
开启事务命令
start transaction;
回滚事务:将数据回复语到事务开始时候的状态
rollback;
提交事务:对事务中的进行操作,进行确认操作,事务在提交后,数据就可恢复。
commit;
方式二:数据库中存在了一个自动变量,通过
show variables like '%commit%';
可以查看,如果autocommit的值是on,说明开启自动提交。
关闭自动提交,
set autocommit = off; 或 set autocommit = 0;
如果甚至autocommit为off,意味着以后每条SQL都会处于一个事务中,相当于每条SQL执行前,都会执行start transaction开启事务命令。
【提示】Oracle中的autocommit默认就是off。
四、JDBC中使用事务
当JDBC程序向数据库获得一个Connection对象,默认情况下,这个Connection对象会自动向数据库提交在它上面发送的SQL语句。如果想关闭这种默认提交方式,让多条SQL在一个事务中执行,则可以使用下面的语句。
JDBC控制事务语句
connection.setAutoCommit(false);//相当于start transaction
connection.rollback();//相当于rollback
connection.commit();//相当于commit
五、事务的特性(ACID)
☆原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部发生,要么全部失败。
【提示】:现在物理学证明,夸克是最小的单位,但是在夸克证明之前,人们普遍认为原子是最小的单位,用原子来描述事务,很贴切。
☆一致性(Consistency)
事务前后数据的完成性必须保持一致。
☆隔离性(Isolation)
事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务干扰,多个并发事务之间数据要相互隔离
☆持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
六、事务的隔离级别
多个线程开启各自事务操作数据库中数据的时候,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。
数据库共定义了四种隔离级别:
serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)
repeatable read:可避免脏读、不可重复读情况的发生(可重复读) ,不可以避免虚读。
read committed:可避免脏读情况发生(读已提交)
read uncommitted:最低解绑,什么也无法保证。(读未提交)
设置事务隔离级别
set session transaction isolation level xxxx;
查询当前事务隔离级别
select @@tx_isolation;
查看MySQL默认的隔离级别:
如果不考虑隔离性,可能会引起如下问题:
6.1 脏读
一个事务读取了另一个事务未提交的数据。
这是非常危险的,假设aaa向bbb转账100元,对应SQL语句如下:
update account set money = money - 100 where name = 'aaa';
update account set money = money + 100 where name='bbb';
先设置事务的隔离级别为read uncommited
set session transaction isolation level read uncommitted;
执行的SQL语句如下:
语句1:
update account set money = money + 100 where name = 'bbb';
语句2:
update account set money = money -100 where name = 'aaa';
当第1条SQL执行完毕,第2条还没执行(没提交时),如果此时B查询自己的账户,就会发现自己多个100元,如果A等B走后再回滚,B就会损失100元。
6.2 不可重复读(强调的是update)
在一个事务内读取表中某一行数据,多次读取结果不同。
例如银行想查询A账户余额,第一次查询A账户为200元,此时A向账户存了100元并提交了。银行接着又进行了一次查询,此时A账户为300元了。银行两次查询不一致,可能就会很困惑,不知道那次查询是准确的。
和脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。
很多人认为这种情况就是对的,无需困惑,当然是后面的为准。我们可以考虑到如下的情况,比如银行程序 需要将查询结果分别输出到电脑屏幕和写到文件中,结果在一个事务中针对输出的目的地,进行的两次查询不一致,导致文件和屏幕中的结果不一致,银行工作人员就不知道以哪个为准了。
6.3虚读(幻读)
一个事务内读取了别的事务插入的数据,导致前后读取不一致。
例如,第一次读取,存在5条记录,然后(另一个事务)向表中插入一条新的记录,第二次读取,存在6条记录。
JDBC程序中能否指定事务的隔离级别 ?
Connection接口中定义事务隔离级别四个常量:
static int TRANSACTION_READ_COMMITTED
指示不可以发生脏读的常量;不可重复读和虚读可以发生。
static int TRANSACTION_READ_UNCOMMITTED
指示可以发生脏读 (dirty read)、不可重复读和虚读 (phantom read) 的常量。
static int TRANSACTION_REPEATABLE_READ
指示不可以发生脏读和不可重复读的常量;虚读可以发生。
static int TRANSACTION_SERIALIZABLE
指示不可以发生脏读、不可重复读和虚读的常量。
通过 void setTransactionIsolation(intlevel) 设置数据库隔离级别
七、事务的丢失更新问题
两个事务或多个事务更新同一行,但这些事务彼此之间都不知道其他事务进行的修改,因此第二个更改会覆盖了一个修改。
丢失更新问题的解决:
悲观锁(假设丢失更新一定会发生)--利用数据库内部锁机制,管理事务
MySQL数据库内部提供两种常用的锁机制:共享锁和排它锁。
允许一张数据表中数据记录,添加多个共享锁,添加共享锁记录,对于其他事务可读不可写。
一张数据表中数据记录,只能添加一个排它锁,在添加排它锁的数据,不能再添加剂其他共享锁和排它锁,对于其他事务是可读不可写的。
所有数据记录修改操作,自动为数据添加排它锁。
添加共享锁方式:select * from account lock in share mode;
添加排它锁方式:select * from account for update;
锁必须在事务中添加,如果事务结束了,锁就释放了。
乐观锁(假设丢失更新不一定会发生)--采用记录的版本×××,来判断记录是否修改过--timestamp
timestamp是可以自动更新的。
create table product( id int, name varchar(20), updatetime timestamp );
insert into product values(1,'哈哈',null); update product set name='呵呵' where id = 1;
timestamp在插入和修改的时候,都会自动更新为当前时间。
解决丢失更新,在数据表中添加版本字段,每次修改记录后,版本字段都会更新,如果读取的是版本字段和修改时的版本字段不一致,说明别人进行修改过数据。
0
收藏
Ctrl+Enter 发布
发布
取消