宏观
并发造成什么问题?
- 脏读 : 一个事物读取了另一个事物未提交的数据
- 不可重复读 : 一个事物多次读取一行数据导致前后数据不一致
- 幻读: 一个事物读取了别的事物插入的数据
后面会详细的讲解为什么,O(∩_∩)O嘿嘿~
模拟事物隔离级别
事物特性
- 原子性A ,指的是不可再次分割, 也就是A和B事物要么成功, 要么失败
- 一致性C: 表示,事物读取数据要保证正确,一致性
- 隔离性I: 事物A不可以对事物B的数据进行更改
- 持久性D: 事物A操作之后数据存储在了磁盘上,不会丢失
隔离级别
- 读未提交: 脏读, 不可重复读, 幻读
- 读已提交: 不可重复读, 幻读
- 可重复读 : 幻读
- 串行化 : 安全, 都没有
隔离级别举栗子
首先根据下面的sql结构测试栗子
-- ----------------------------
-- Table structure for t_student
-- ----------------------------
DROP TABLE IF EXISTS `t_student`;
CREATE TABLE `t_student` (
`sno` int(11) NOT NULL,
`sname` varchar(255) CHARACTER SET gbk DEFAULT NULL,
`sdree` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`ssex` char(255) CHARACTER SET utf8 DEFAULT NULL,
`sage` decimal(10,0) DEFAULT NULL,
PRIMARY KEY (`sno`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of t_student
-- ----------------------------
INSERT INTO `t_student` VALUES ('1', 'judy', '天融信', '男', '26');
INSERT INTO `t_student` VALUES ('2', '杜雨', '中银', '男', '26');
INSERT INTO `t_student` VALUES ('3', '雅雯雯', '买卖宝', '男', '27');
INSERT INTO `t_student` VALUES ('4', '子腾', 'IBM', '男', '23');
INSERT INTO `t_student` VALUES ('5', '旭日', '微软', '女', '25');
1 查看当前数据库隔离级别, 设置隔离级别
SELECT @@tx_isolation;
-- 非正常情况下
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
读未提交出现的并发问题
A 窗口执行sql
start transaction;
select * FROM t_student WHERE sno ='1';
结果:
1 judy 天融信 男 26
B 窗口执行sql
start transaction;
select * FROM t_student WHERE sno ='1';
UPDATE t_student SET sname='wangxuefen' WHERE sno =1;
执行后的结果
1 wangxuefen 天融信 男 26
A 窗口执行查询语句(这句话出现的问题)
select * FROM t_student WHERE sno ='1';
得到结果: 1 wangxuefen 天融信 男 26
B 窗口执行
ROLLBACK;
select * FROM t_student WHERE sno ='1';
得到的结果是
1 judy 天融信 男 26
A 窗口执行
select * FROM t_student WHERE sno ='1';
得到的结果是
1 judy 天融信 男 26
从最后的执行结果中我们可以发现A读取了B未提交的数据, 当B事物回滚的时候, A之前读到的数据变成了脏数据, 这就是脏读
提交读出现的问题
1 更改事物的隔离级别
SELECT @@tx_isolation;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
A 窗口
start transaction;
select * FROM t_student WHERE sno ='1';
执行结果
1 judy 天融信 男 26
B 窗口
start transaction;
UPDATE t_student SET sname='12' WHERE sno =1;
A 窗口 (出现问题的原因,B还没有提交但是已经读了,发送不可重复读问题)
select * FROM t_student WHERE sno ='1';
执行结果
1 judy 天融信 男 26
B 窗口 (出现问题的原因)
COMMIT
A 窗口
select * FROM t_student WHERE sno ='1';
执行结果
1 12 天融信 男 26
读已提交,出现不可重复读问题
可重复读 REPEATABLE-READ(mysql默认级别)
A 窗口
start transaction;
SELECT * FROM t_student WHERE sno='1'
执行结果:1 judy 天融信 男 26
B 窗口
start transaction;
SELECT * FROM t_student WHERE sno='1';
UPDATE t_student SET sname='wxf' WHERE sno =1;
SELECT * FROM t_student WHERE sno='1';
执行结果
1 wxf 天融信 男 26
A 窗口 (这里可以跟读已提交隔离级别进行对比)
1 judy 天融信 男 26
B 窗口
COMMIT
A 窗口
SELECT * FROM t_student WHERE sno='1'
得到结果:
1 judy 天融信 男 26
从得出的结果可以看出我们已经避免了可重复读的问题, 但是发现最后一次的A窗口得到的结果却不是我们想要的数据,也就是不是最后更改后的数据.
事物隔离级别设置为串行化 Serializable
事物隔离级别是最为安全的,设置隔离级别
SET SESSION tx_isolation='SERIALIZABLE';
A窗口
start transaction ;
B 窗口
start transaction ;
A窗口
SELECT * FROM t_student WHERE sno='1';
得到结果:1 wxf 天融信 男 26
B窗口
UPDATE t_student SET sname='judy' WHERE sno =1
得到结果: 被卡住了, 无法得到结果
为什么会出现被卡住呢? 原因是因为当A去开启事物并且去执行查询事件, 这个时候B去执行更新操作,那么这个时候会被卡住,原因是因为A的select操作被上了锁, 所以B窗口的update操作是无法执行的, 所以就解决了我们上述所有问题,简单粗暴~
悲观锁
悲观锁分为共享锁和排它锁
什么是共享锁
共享锁的意思是多个事物对一行数据操作的时候, 可以共享一把锁,都可以访问到数据,但是只能读不能更改,写.
排他锁的意思是指当有一行数据是排他锁的时候, 其他事物就不能再获取该锁的其他锁,包括共享锁和排它锁,
A 窗口 ,(FOR UPDATE指的是悲观锁)
START TRANSACTION;
SELECT * FROM t_student WHERE sno ='1' FOR UPDATE
B 窗口
START TRANSACTION;
SELECT * FROM t_student WHERE sno ='1' FOR UPDATE
执行结果:
被卡住了
乐观锁
依靠数据本身来保证数据的正确性
乐观锁的两种方式
1 使用版本号
2 使用时间戳
A 窗口
SELECT * FROM t_student WHERE sno='1'
UPDATE t_student SET sname='judy',version=version+1 WHERE sno='1'
B窗口
SELECT * FROM t_student WHERE sno='1'
UPDATE t_student SET sname='judy',version=version+1 WHERE sno='1' AND version =1