事务的作用是将一系列操作作为一个整体,一但其中出现问题,会回滚到事务的开始状态。即事务维护了数据的完整性和一致性。
如果不考虑隔离性,事务会出现以下问题。
脏读又称无效数据的读出,是指在数据库访问中,事务T1将某一值修改,然后事务T2读取该值,此后T1因为某种原因撤销对该值的修改,这就导致了T2所读取到的数据是无效的。脏读是读到了另一个事务的中为commit的脏数据,所以称为脏读。
重现过程,将数据库隔离界别设为read-committed
,打开两个数据库连接,连接a去开启事务,insert 一个调数据,连接b查询出这条数据,连接a提交rollback,连接b再查询。
connect_A > begin;
connect_A > insert into app(app_code,app_name,customer_code,latest_version_code,client_version,create_time) values ('1','1','003','1.0','1.0',now());
connect_B > select app_name from app;
connect_A > rollback;
connect_B > select app_name from app;
不可重复读是指一个线程中的事务读取到了另外一个线程中提交的update的数据。
如事务t1查询某人员数据,这时事务t2修改了该人员数据,t1再次查询该人员数据,发现该事务中两次查询的数据不一致。
重现过程,将数据库隔离设为read-uncommitted,打开两个数据库连接,连接a开启事务,查询表信息,连接b修改一条数据,连接a再查表记录,导致连接a两次查询数据不一致。
connect_A > begin;
connect_A > select app_name from app;
connect_B > update app set app_name='2' where app_code='1';
connect_A > select app_name from app;
connect_A > commit;
虚读是指一个线程中的事务读取到了另外一个线程中提交的insert的数据。
如事务t1查询人物数据有6条,事务t2插入1条数据,事务t1再查询人物数据就为7条了。
重现过程,将数据库隔离级别设为repeatable-read,打开两个数据库连接,连接a开启事务,查询表记录,连接b插入一条表记录,连接a再次查询表记录。
connect_A > begin;
connect_A > select app_name from app;
connect_B > update app set app_name='2' where app_code='1';
connect_A > select app_name from app;
connect_A > commit;
不可重复读和虚读都是事务在执行过程中,有其他事务修改了数据,不过不可重复读是修改,而虚读是添加或删除。
在事务中,使用rollback或commit作为事务结束的标志,如果事务未提交就关闭了,需要手动查询出事务进程,并杀掉
sql > show processlist;
sql > kill xx;
下面是隔离级别与数据问题的关系
读未提交(Read uncommitted) | 读已提交(Read committed) | 可重复读(Repeatable read) | 串行化(Serializable) | |
---|---|---|---|---|
脏读 | 会发生 | 不会发生 | 不会发生 | 不会发生 |
不可重复读 | 会发生 | 会发生 | 不会发生 | 不会发生 |
虚读(幻读) | 会发生 | 会发生 | 会发生 | 不会发生 |
MySQL支持以上四中隔离级别,默认隔离级别是Repeatable read;Oracle只支持串行化(Serializable)、读已提交(Read committed),默认是读已提交(Read committed)级别。
MySQL查看当前隔离级别:
select @@tx_isolation;
MySQL设置隔离级别语句
set [glogal | session] transactionisolation level 隔离级别名称;
或
set tx_isolation='隔离级别名称';
如
set tx_isolation='read-committed'
数据库的锁有X锁(排他锁),S锁(共享锁)。每个数据库的查询执行前,都必须要拿到锁才能执行。当一个连接拿到X锁(排他锁)时,其他的连接都拿不到锁了,要等这个连接的X锁解锁。如果一个连接拿到了S锁,其他数据库连接还是能拿到S锁的,互不干扰。
锁的粒度类型有record lock(行锁),gap lock(间隙锁)。行锁是按照索引锁住这个索引对应的数据。间隙锁是锁住这个索引以上的或以下的锁,就是范围锁。
在修改时修改数据开启X锁,事务提交关闭。事务只读不修改是不用加锁的。可以保证提交数据丢失的情况。可能会出现脏读,不可重复读,虚读。
在事务中,第一级封锁协议加上查询时对相应的数据添加S锁,查询结束关闭。这样可以保证事务读取的数据都是事务已经提交的,解决脏读问题
在事务中,第一级封锁协议加上查询对应数据添加S锁,S锁直到事务提交。可以解决可重复的问题。
mysql通过(read view),mvcc实现避免幻读。即读的数据不是最新的数据,而是之前的快照。
查询使用S锁,修改时对应行数据使用X锁,修改结束释放X锁。
查询使用S锁,查询结束,释放S锁。修改时,对应行数据使用X锁,直到事务结束,释放X锁。
使用MVVC。
该隔离级别会在读取的每一行数据上都加上锁,退化为基于锁的并发控制,即LBCC。使用的间隙锁,所以能解决幻读。