事务的四大特性
一提到数据库事务,没错,我们首先会想到ACID,也就是事务的四个特性。
A原子性 C一致性 I隔离性 D持久性
与之带来的问题
具体内容不再赘述,事务的这四个特性会带来什么问题呢?
脏读,不可重复读,幻读
1.脏读:一个事务读取到另外一个事务未提交的内容
2.不可重复读:一个事务内前后两次读取到的内容不一致。产生的原因是该事务前后两次读取的时间段内,另外一个事务对该条数据进行了修改。
3.幻读:一个事务A进行范围查询时得到M条数据,另一个事务B在符合事务A的范围内插入了N条数据,导致事务A再次进行查询时会得到M+N条数据,产生幻读。
不可重复读和幻读有类似之处,本质差别在于,前者针对update,后者针对insert
MYSQL的四种隔离级别:
1.读未提交(READ UNCIMMITTED):可以读取别的事务未commit的内容,会导致脏读的问题。
2.读已提交(READ COMMITTED):可以读取别的事务已commit的内容,会导致不可重复读的问题。常用的就是该级别。
3.可重复读(REPEATABLE READ):可以保证该事务内,前后两次读取的内容一致。即使另一事物对该条数据进行了修改。
4.可串行化(SERIALIZABLE):可串行化是事务的最高隔离级别,事务在读取或者修改表数据时,都会增加表级锁,防止幻读。与此同时,会大大影响性能。
这四种隔离级别可根据自身业务需要进行设置,最常用的是READ COMMITTED。
锁
事务的隔离级别主要是通过锁来控制的,下面介绍一下锁。
不同的存储引擎对锁的处理方式不一样,MYSQL5版本之后默认采用Innodb,InnoDB支持表锁和行锁。
下面进行几个实验:
准备:
1.建表
CREATE TABLE
user_info
(
userid VARCHAR(40) NOT NULL ,
username VARCHAR(40),
sex INT(1),
certtype VARCHAR(10),
certid VARCHAR(20),
phoneno VARCHAR(11),
age INT(3),
PRIMARY KEY (userid),
INDEX certtype_id (certid, certtype)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.准备几条数据
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('000007c2b80f11e99a9454ee755d5b38', 'demo', 1, 'Ind02', '539452823381361', '13756168529', 66);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('00000a39b81511e99a9454ee755d5b38', 'fru', 1, 'Ind02', '535322313695514', '14297850313', 38);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('00000ba7b81011e99a9454ee755d5b38', 'hlz', 1, 'Ind02', '535322313695514', '14027724002', 85);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('0000161bb81911e99a9454ee755d5b38', 'diu', 1, 'Ind02', '838336353039682', '14279925806', 38);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('0000197cb81811e99a9454ee755d5b38', 'dsf', 1, 'Ind02', '748854264383068', '14433861050', 42);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('00004862b81311e99a9454ee755d5b38', 'udi', 1, 'Ind02', '533297220741675', '13796868687', 88);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('000052bab81611e99a9454ee755d5b38', 'l', 1, 'Ind02', '119285593851735', '14114443115', 71);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('0000580eb81111e99a9454ee755d5b38', 'hhi', 1, 'Ind02', '644459799532275', '14241303402', 77);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('00005edfb81711e99a9454ee755d5b38', 'fwu', 1, 'Ind02', '792649599530407', '14394181380', 9);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('00007186b81211e99a9454ee755d5b38', 'irl', 1, 'Ind02', '112327407218820', '13599040961', 15);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('0000808fb81411e99a9454ee755d5b38', 'ufi', 1, 'Ind02', '185371452183808', '13841946470', 15);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('00008719b80e11e99a9454ee755d5b38', 'wre', 2, 'Ind02', '707119444112404', '13864141398', 69);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('00008c62b81011e99a9454ee755d5b38', 'udh', 1, 'Ind02', '978111704791068', '14234926100', 74);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('00008f70b80f11e99a9454ee755d5b38', 'hus', 1, 'Ind02', '948014209929867', '14153437874', 42);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('00009aecb81511e99a9454ee755d5b38', 'uey', 1, 'Ind02', '376089044265532', '14118677569', 96);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('0000a956b81911e99a9454ee755d5b38', 'off', 1, 'Ind02', '179309141989153', '13898109495', 45);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('0000c7acb81811e99a9454ee755d5b38', 'eed', 1, 'Ind02', '928275743432599', '13858352803', 0);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('0000c918b81a11e99a9454ee755d5b38', 'ggi', 1, 'Ind02', '705640666843988', '13662965102', 69);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('0000cbbcb81611e99a9454ee755d5b38', 'yfu', 1, 'Ind02', '56476028688695', '13849848820', 57);
INSERT INTO user_info (userid, username, sex, certtype, certid, phoneno, age) VALUES ('0000d9bdb81111e99a9454ee755d5b38', 'di', 1, 'Ind02', '134768997444556', '14454071981', 36);
3.开始实验
开启一个事务,将这行数据的username更新为“fru1”
start transaction; --开启事务
update user_info set username='fru1' where userid='00000a39b81511e99a9454ee755d5b38'; --更新数据,锁数据
暂不提交或者回滚
另外开启一个session,也就是重新打开一个窗口。执行下列语句,将名字更新为“fru2”
update user_info set username='fru2' where userid='00000a39b81511e99a9454ee755d5b38';
效果是什么呢?
卡在这里了,无法更新,前一个事务提交后,后一个事务继续执行。执行后的结果:
更新成功。 这个很容易理解,第一个事务中,update语句加了行锁。
这个理解了,暂且一放,进行下一个实验。
执行下一个语句:
start transaction;
update user_info set username='fru1' where username='fru2';
在另一个窗口执行:
update user_info set username='fru2' where userid='0000161bb81911e99a9454ee755d5b38';
执行效果如下:
卡住了!!加锁了?但是更新的不是一条数据为什么也会被加锁?
原因是:
在mysql中,InnoDB实现行锁并是不锁记录,而是锁索引。如果一条语句,操作了主键索引,MYSQL就是锁该条主键索引。如果操作了非主键索引,MYSQL会先索引该非主键索引,再通过非主键索引,锁定相关主键索引。如果操作不是通过索引,那就会锁表。
所以后面的crud操作中,更新语句是不是会去留意一下这点呢?如果留意了,我的目的也就达到了