开发者学堂课程【数据库核心概念:锁(三)】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/63/detail/1146
锁(三)
内容介绍
一、简介
二、行锁定基本演示
一、简介
首先介绍行锁,行锁偏向于 Innodb 存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。假设一个人使用72行,另一个人使用38行,两个人之间没有交集。
所以发生所冲突的概率最低,并发度也最高。InnoDB 与 MyISAM 的最大不同有两点:
一是支持事务(TRANSACTION) ;二是采用了行级锁
此时由于行锁支持事务,在笔试题选择题当中考试的较多。对于数据库的事务及Acid概念,考试频率较高,例如填空题:
对于事物及 ACID,请分别作答 ACID 表示的含义;或是选择题持久性所代表的含义是 JAVA 程序员的基础。
事务概念如下:
事务是由一组 SQL 语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性。
1.原子性(Atomicity) :事务是-一个原子操作单元,其对数据的修
改,要么全都执行,要么全都不执行。
2.一致性(Consistent) :在事务开始和完成时,数据都必须保持一
致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束时,所有的内部数据结构(如B树索引或双向链表)也都必须是正确的。
3.隔离性(Isolation) :数据库系统提供一 定的隔离机制,保证事务
在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。
4.持久性(Durable) :事务完成之后,它对于数据的修改是永久性的,
即使出现系统故障也能够保持。
事物与原子操作相提并论,是一起成功,一起失败的,最明显的是转账的case。并发事务会带来一些问题,常见的问题如下:
更新丢失(Lost Update)
脏读(Dirty Reads)
不可重复读(Non-Repeatable Reads)
幻读(Phantom Reads)
更新丢失概念如下:
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题
--最后的更新覆盖了由其他事务所做的更新。
例如,两个程序员修改同- java 文件。每程序员独立地更改其副本,然后保存更改后的副本,这样就覆盖了原始文档。最后保存其更改副本的编辑人员覆盖前-一个程序员所做的更改。
如果在一个程序员完成并提交事务之前,另一个程序员不能访问同一文件,则可避免此问题。
脏读的概念如下:
一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不-致状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一 步的处理,就会产生未提交的数据依赖关系。这种现象被形象地叫做”脏读”。
一句话:事务 A 读取到了事务B已修改但尚未提交的的数据,还在这个数据基础上做了操作。此时,如果 B 事务回滚,A读取的数据无效,不符合一致性 要求。
也许数据错误,读到错误的数据之后使用。错误数据进行操作,那么程序就会挂。
不可重读概念如下:
一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变,或某些记录已经被删除了!这种现象就叫做 “不可重复读”。
一句话: 事务 A 读取到了事务B已经提交的修改数据,不符合隔离性。
幻读概念如下:
一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“ 幻读”。
一句话:事务 A 读取到了事务 B 体提交的新增数据,不符合隔离性。
幻读和脏读类似,脏读是事务 B 里面修改了数据,幻读是事务 B 里面新增数据。
My SQL 的默认隔离级别是可冲突,用事物的隔离级别来解决以上问题。事物的隔离级别有四种,分别是未提交读,已提交读,可重复读,可序列化,从上到下级别越来越高。序列化之后就不会有并发问题,但是性能会挂。My SQL 出厂时所带的默认级别如下:
结合事物的隔离级别,得出 MySQL 是可重复读的默认级别,此时其读数据一致性是事物级别,不借助其他软件或其他服务,默认 MySQL 是避免了脏读和不可重复读,但是 MySQL 会存在幻读,但此时也可以进行优化。
在学习 spring 时,有注解标签,在注解标签当中要配置 socialism,默认的隔离级别选择 default,Default 是数据库当中的内容,不是使用 JAVA 设置。MySQL 的默认级别是rr,简称可重复读。MySQL 在一般情况下不应该出现脏读,不可重复读,有可能会出现幻读。
相关行锁对应的处理和优化,案例分析包括以下内容:
1.建表 SQL
2.行锁定基本演示
3.无索引行锁升级为表锁
4.间隙锁危害
此时使用的引擎是 INNODB,表明 test_innpdb_lock,一个整型,一个 varchar。此时为 a 字段单独建立了索引:
create index test_ _innodb_ a_ _ind on test_ _innodb_ lock(a);
为b字段建立了引擎:
create index test_ innodb_ lock_ b_ ind on test_innodb_lock(b);
是单指索引,将代码执行之后建表完成。
二、行锁定基本演示
5.5之后,默认数据库引擎是 INNODB,也就是事务的 commit 提交每一个分号,只要是写操作就会自动提交,相当于把自动提交关闭:
Set autocommit=0;
此时自动提交关闭,必须手动提交。设置为0之后,先更新表格,执行如下代码:
Select * from test_ innodb_ lock;
此时加入的是行锁,只将一行锁定。执行如下代码:
update test_ innodb_ 1ock set b= " 4001’ where a=4;
此时如果进行回车,不会出现问题,要操作表格的第四行。此时与之前相同,只是发送了一个查询语句。
此时查看第四条记录是否是4001,执行如下代码:
Select * from test_ innodb_ lock;
结果如下:
此时在 session2 当中执行如下语句:
Select * from test_ innodb_ lock;
结果如下:
“读己之所写”这句话的意思就是读自己所写的内容。Session1 就是读己之所写,将自动提交关闭。根据系统rr的隔离级别是,不应该出现脏读。别人没有提交的数据,Session2 无法读取。以上就是读己之所写的含义。例如在新浪微博当中用手机照照片,发一条微博,此时发送,但是地球上所有用户不能全都看到。新浪是亿级别的分布式架构系统。在学 NOsql CAP 理论时,在强硬执行和 a 高可用之间做了选择。
例如淘宝网站和京东网站只能选 cp,而不能选 ap。大型网站一定是分布式系统,此时由于 NOsql CAP 理论中,p 必须要保证,只能够在 a 高可用性和 c 强移植性当中做选择。假设选择 a 此时就需要牺牲 c 强移植性。演示如下,手机新浪微博发一条微博,获取到的第一个人是自己,如果自己发信息,自己看不到,那么这个产品是不合格的。所以需要保证读几只所写,但不见得发出之后地球上所有人都能够看到。
对于分布式系统应该存在延时,一定时间过后更多用户才能读取到。不能要求立即发送之后所有人都能看到,强移植性无法做到,违反 cap 理论。但由于存在双十一时间点,用户较多,会导致系统瘫痪,A 高可用不足以保证,登陆电商网站并不关心的有多少人对商品点赞,有多少人浏览过该产品等等数据的强移植性,应该先保证 a,保证该网络不瘫痪。此时数据移植性可以弱于高可用性。回到机新浪微博,对于 c 移植性,发微博之后所有人都需要看到,就需要牺牲 A 保证 C。
第二种是存在缓冲时间,也就是延时,先保证 A 高可用整个网站的可用性,数据移植性在合理范围之内的延时,此时用户能够接受。以上就是 cap 理论,保 cp 还是保 ap 的绝对值。对于大型网站一定只能保 ap。对于数据的移性,最后再将数据修复和存储。回到测试当中,如果 session1 当中没有 commit 提交,那么在 session2 当中仍然查询不到任何修改的数据。此时执行如下代码:
Commit;
提交之后在 session2 中查询:
Select * from test_ innodb_ lock;
结果如下:
执行如下代码查询第四条数据:
Select * from test_ innodb_ 1ock where a=4;
此时仍然没有改变,因为还没有提交成功。在 session1 中执行如下代码:
Commit;
在 session2 当中执行如下代码:
Commit;
执行完成之后,查询,执行如下代码:
Select * from test_ innodb_ 1ock where a=4;
结果如下:
结果被改变。此时在 session1 执行如下代码:
update test_ innodb_ 1ock set b= ' 4002’where a=4;
在 session2 当中执行如下代码:
update test_ innodb_ 1ock set b='4003’ where a=4;
行锁只执行到第四行,进行修改并未提交之后,现在关心的内容是在 session2 当中是否会改变,在 session2 当中执行如下代码:
update test_ innodb_ 1ock set b='4003’ where a=4;
产生阻塞。所以只能在 session1 当中提交。执行如下代码:
Commit;
在 session2 当中也执行如下代码:
Commit;
也就是在 session1 当中修改为4002之后,在 session2 当中也需要进行提交。多次执行commit之后,保证两边都提交完成。执行如下代码查询:
Select * from test_ innodb_ lock;
结果如下:
在 session2 当中执行如下代码:
Select * from test_ innodb_ 1ock where a=4;
查询结果如下:
在 session2 当中执行如下代码:
Select * from test_ innodb_ lock;
查询结果如下:
提交更新之后解除了阻塞,之后的更新才能正常进行。此时将其更新为4005。在session1 中执行如下代码:
update test_ innodb_ lock set b=' 4005",where a=4;
在 session2 当中执行如下代码:
select" from test_ innodb_ lock ;
结果如下:
之前加入的是行锁,争抢第四条记录。现在 session1 更改第四条记录,session2 更改第四条记录, 在 session2 当中,修改时会阻塞,因为并没有提交。但是如果session2更改第四条记录,session1 更改第四条记录,就不会产生阻塞。
演示如下:
在 session1 中执行如下代码:
update test_ innodb_ 1ock set b=' 4005’ where a=4;
在之前,如果没有提交 session2,如果需要争抢第四条记录,那么只会阻塞,但如果是修改第九条记录就不会阻塞。由于执行了如下代码:
Set autocommit=0;
所以在两部分都需要提交。如果提交顺利,session1 就是4005,session2 就是9001。结果如下:
行锁的作用就是将两部分分开,以上就是行锁基本演示。