首页 > 数据库技术 > 详细

数据库笔记

时间:2019-03-11 13:08:35      阅读:171      评论:0      收藏:0      [点我收藏+]
1、分库分表技术演进&最佳实践     
  https://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&mid=2247486161&idx=1&sn=a8b68997a8e3e1623e66b83d5c21ce88&chksm=9bd0a749aca72e5f240a6ad1b28bcc923ee2874e16d9b9641b7efd99bc368baa963d081e2ba0&scene=21#wechat_redirect

2、为什么分库分表后不建议跨分片查询
  https://mp.weixin.qq.com/s/l1I5u3n-lSwDYfxC-V3low

3、Mybatis相关博客
  https://www.cnblogs.com/xiaoxi/category/929946.html

mysql博客:
  https://www.cnblogs.com/duanxz/tag/mysql/default.html?page=2

4、分分钟解决 MySQL 查询速度慢与性能差
  https://mp.weixin.qq.com/s?__biz=MzU2NjIzNDk5NQ==&mid=2247485663&idx=1&sn=ca4b6b8b9f1f07130991ebf4280f3516&chksm=fcaed2c3cbd95bd5a7c0edad1570e4aa6743a31b737237d1614ad1e53cfbc791cbe00ef1718c&scene=0&key=cb077656fa10b4eb22cbad0217b717b7810f0bd671047098e38bf3bcce15fc365c684d12a527db0f7b05a091833f15adb8955eac5bab0b1facdb9c3777f8a313f5fbd5bb647bce8d27569780d0529eb0&ascene=1&uin=MjU3OTI0NDc0Mg%3D%3D&devicetype=Windows+10&version=62060720&lang=zh_CN&pass_ticket=bUI0a1DMiTeed1JhWLPDXnic6pU3sSLG%2BHlhGuYNEkgGE66FurCt3ct9gbyFdnrz

5、深入学习MySQL事务:ACID特性的实现原理*** https://www.cnblogs.com/kismetv/p/10331633.html
4种事务的隔离级别,InnoDB如何巧妙实现? https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961498&idx=1&sn=058097f882ff9d32f5cdf7922644d083&chksm=bd2d0d468a5a845026b7d2c211330a6bc7e9ebdaa92f8060265f60ca0b166f8957cbf3b0182c&scene=21#wechat_redirect
(1)原子性(Atomicity):原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做;
如果事务中一个sql语句执行失败,则已执行的语句也必须回滚,数据库退回到事务前的状态。
实现原子性的关键,是当事务回滚时能够撤销所有已经成功执行的sql语句。
InnoDB实现回滚,靠的是undo log:当事务对数据库进行修改时,InnoDB会生成对应的undo log;
如果事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。
以update操作为例:当事务执行update时,其生成的undo log中会包含被修改行的主键(以便知道修改了哪些行)、修改了哪些列、这些列在修改前后的值等信息,回滚时便可以使用这些信息将数据还原到update之前的状态。
(2)持久性(Durability):持久性是指事务一旦提交,它对数据库的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
当数据修改时,除了修改Buffer Pool中的数据,还会在redo log记录这次操作;
当事务提交时,会调用fsync接口对redo log进行刷盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。
redo log采用的是WAL(Write-ahead logging,预写式日志),所有修改先写入日志,再更新到Buffer Pool,保证了数据不会因MySQL宕机而丢失,从而满足了持久性要求。
(3)隔离性(Isolation)隔离性是指,事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
一个事务写操作对另一个事务写操作的影响:锁机制保证隔离性
一个事务写操作对另一个事务锁读操作的影响:锁机制保证隔离性
一个事务写操作对另一个事务读操作的影响:MVCC保证隔离性
InnoDB实现了四种不同事务的隔离级别:读未提交(Read Uncommitted),读提交(Read Committed, RC),可重复读(Repeated Read, RR),串行化(Serializable)
可重复读是InnoDB默认的隔离级别,在RR下:
(1)普通的select使用快照读(snapshot read),底层使用MVCC来实现,MVCC全称Multi-Version Concurrency Control,即多版本的并发控制协议。在同一时刻,不同的事务读取到的数据可能是不同的(即多版本)
MVCC最大的优点是读不加锁,因此读写不冲突,并发性能好。
InnoDB实现MVCC,多个版本的数据可以共存,主要是依靠数据的隐藏列(也可以称之为标记位)和undo log。
其中数据的隐藏列包括了该行数据的版本号、删除时间、指向undo log的指针等等;
当读取数据时,MySQL可以通过隐藏列判断是否需要回滚并找到回滚需要的undo log,从而实现MVCC。

脏读:当事务A、B均在T1时间开启事务,事务B在T2时间修改了数据,当事务A在T3时间节点读取该行数据时,会发现数据已被其他事务修改,且状态为未提交。此时事务A读取最新数据后,根据数据的undo log执行回滚操作,得到事务B修改前的数据,从而避免了脏读。

不可重复读:当事务A在T2节点第一次读取数据时,会记录该数据的版本号(数据的版本号是以row为单位记录的),假设版本号为1;当事务B在T3修改改行并在T4提交时,该行记录的版本号增加,假设版本号为2;当事务A在T5再一次读取数据时,发现数据的版本号(2)大于第一次读取时记录的版本号(1),因此会根据undo log执行回滚操作,得到版本号为1时的数据,从而实现了可重复读。

幻读:使用类似临键锁(next-key lock)的机制
(2)加锁的select(select ... in share mode / select ... for update), update, delete等语句,它们的锁,依赖于它们是否在唯一索引(unique index)上使用了唯一的查询条件(unique search condition),或者范围查询条件(range-type search condition):
1、在唯一索引上使用唯一的查询条件,会使用记录锁(record lock),而不会封锁记录之间的间隔,即不会使用间隙锁(gap lock)与临键锁(next-key lock)
2、范围查询条件,会使用间隙锁与临键锁,锁住索引记录之间的范围,避免范围间插入记录,以避免产生幻影行记录,以及避免不可重复的读

6、详细分析MySQL事务日志(redo log和undo log)
  https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html

7、关于MySQL,你未必知道的!***
  https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961957&idx=1&sn=c4cdf7c27ade9c95fdf40c4c38e19da9&chksm=bd2d0fb98a5a86af13ec7f096bde37e1c8cd0d19e7124e6bdb53761314d5b64a39ba9fbd1355&scene=21#wechat_redirect

8、数据库索引,到底是什么做的?
   https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961486&idx=1&sn=b319a87f87797d5d662ab4715666657f&chksm=bd2d0d528a5a84446fb88da7590e6d4e5ad06cfebb5cb57a83cf75056007ba29515c85b9a24c&scene=21#wechat_redirect
加速查找速度的数据结构,常见的有两类:
(1)哈希,例如HashMap,查询/插入/修改/删除的平均时间复杂度都是O(1);
(2)树,例如平衡二叉搜索树,查询/插入/修改/删除的平均时间复杂度都是O(lg(n));
对于这样一个单行查询的SQL需求:
select * from t where name=”shenjian”;
确实是哈希索引更快,因为每次都只查询一条记录。
画外音:所以,如果业务需求都是单行访问,例如passport,确实可以使用哈希索引。
但是对于排序查询的SQL需求:
分组:group by
排序:order by
比较:<、>
哈希型的索引,时间复杂度会退化为O(n),而树型的“有序”特性,依然能够保持O(log(n)) 的高效率
InnoDB并不支持哈希索引。

9、索引的实现原理
  https://blog.csdn.net/timer_gao/article/details/78013826
https://www.cnblogs.com/weiwenbo/p/6531977.html
B+树,它是一种非常适合用来做数据库索引的数据结构:
(1)很适合磁盘存储,能够充分利用局部性原理,磁盘预读;
(2)很低的树高度,能够存储大量数据;
(3)索引本身占用的内存很小;
(4)能够很好的支持单点查询,范围查询,有序性查询;

10、1分钟了解MyISAM与InnoDB的索引差异
  https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961494&idx=1&sn=34f1874c1e36c2bc8ab9f74af6546ec5&chksm=bd2d0d4a8a5a845c566006efce0831e610604a43279aab03e0a6dde9422b63944e908fcc6c05&scene=21#wechat_redirect
MyISAM的索引:
MyISAM的索引与行记录是分开存储的,叫做非聚集索引(UnClustered Index)。
其主键索引与普通索引没有本质差异:
有连续聚集的区域单独存储行记录
主键索引的叶子节点,存储主键,与对应行记录的指针
普通索引的叶子结点,存储索引列,与对应行记录的指针
画外音:MyISAM的表可以没有主键。
主键索引与普通索引是两棵独立的索引B+树,通过索引列查找时,先定位到B+树的叶子节点,再通过指针定位到行记录。
InnoDB的索引:
InnoDB的主键索引与行记录是存储在一起的,故叫做聚集索引(Clustered Index):
没有单独区域存储行记录
主键索引的叶子节点,存储主键,与对应行记录(而不是指针)
画外音:因此,InnoDB的PK查询是非常快的。
因为这个特性,InnoDB的表必须要有聚集索引:
(1)如果表定义了PK,则PK就是聚集索引;
(2)如果表没有定义PK,则第一个非空unique列是聚集索引;
(3)否则,InnoDB会创建一个隐藏的row-id作为聚集索引;
聚集索引,也只能够有一个,因为数据行在物理磁盘上只能有一份聚集存储。
InnoDB的普通索引可以有多个,它与聚集索引是不同的:
普通索引的叶子节点,存储主键(也不是指针)
对于InnoDB表,这里的启示是:
(1)不建议使用较长的列做主键,例如char(64),因为所有的普通索引都会存储主键,会导致普通索引过于庞大;
(2)建议使用趋势递增的key做主键,由于数据行与索引一体,这样不至于插入记录时,有大量索引分裂,行记录移动;

11、InnoDB,5项最佳实践,知其所以然?
  https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961428&idx=1&sn=31a9eb967941d888fbd4bb2112e9602b&chksm=bd2d0d888a5a849e7ebaa7756a8bc1b3d4e2f493f3a76383fc80f7e9ce7657e4ed2f6c01777d&scene=21#wechat_redirect

12、就是这么迅猛的实现搜索需求
  https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651959917&idx=1&sn=8faeae7419a756b0c355af2b30c255df&chksm=bd2d07b18a5a8ea75f16f7e98ea897c7e7f47a0441c64bdaef8445a2100e0bdd2a7de99786c0&scene=21#wechat_redirect

13、MySql数据库索引原理
  https://www.cnblogs.com/weizhixiang/p/5914120.html

14、MySQL如何查询某个字段长度最大的记录
首先查询字段的最大长度值
SELECT max(length(message)) FROM apple;
假设查询结果为60220
接着查询具体的记录
SELECT * FROM apple where length(message)=60220;

15、MySQL 加锁处理分析***
  http://hedengcheng.com/?p=771#_Toc374698322
delete from t1 where id = 10;这样一条sql如何加锁?
(1)如果id是主键,并且事务隔离级别为RC:Read Committed
--- id是主键时,此SQL只需要在id=10这条记录上加X锁即可。
(2)id不是主键,而是一个Unique索引,并且事务隔离级别为RC:Read Committed
--- 若id列是unique列,其上有unique索引。那么SQL需要加两个X锁,一个对应于id unique索引上的id = 10的记录,另一把锁对应于聚簇索引上的[name=’d’,id=10]的记录。
(3)id不是主键,只有一个普通索引,并且事务隔离级别为RC:Read Committed
--- 若id列上有非唯一索引,那么对应的所有满足SQL查询条件的记录,都会被加锁。同时,这些记录在主键索引上的记录,也会被加锁。
(4)id不是主键,只是一个普通字段,没有索引,并且事务隔离级别为RC:Read Committed
--- 若id列上没有索引,SQL会走聚簇索引的全扫描进行过滤,由于过滤是由MySQL Server层面进行的。因此每条记录,无论是否满足条件,都会被加上X锁。
(5)如果id是主键,并且事务隔离级别为RR:Repeatable Read
--- id是主键时,此SQL只需要在id=10这条记录上加X锁即可。
(6)id不是主键,而是一个Unique索引,并且事务隔离级别为RR:Repeatable Read
--- 若id列是unique列,其上有unique索引。那么SQL需要加两个X锁,一个对应于id unique索引上的id = 10的记录,另一把锁对应于聚簇索引上的[name=’d’,id=10]的记录。
(7)id不是主键,只有一个普通索引,并且事务隔离级别为RR:Repeatable Read
--- Repeatable Read隔离级别下,id列上有一个非唯一索引,对应SQL:delete from t1 where id = 10;
首先,通过id索引定位到第一条满足查询条件的记录,加记录上的X锁,加GAP上的GAP锁,然后加主键聚簇索引上的记录X锁,然后返回;
然后读取下一条,重复进行。直至进行到第一条不满足条件的记录[11,f],此时,不需要加记录X锁,但是仍旧需要加GAP锁,最后返回结束。
如果查询的数据不存在,则只加GAP间隙锁,因此其他事务可以执行更新、删除操作,但是无法插入数据。
(8)id不是主键,只是一个普通字段,没有索引,并且事务隔离级别为RR:Repeatable Read
--- 若id列上没有索引,SQL会走聚簇索引的全扫描进行过滤,那么会锁上表中的所有记录,同时会锁上聚簇索引内的所有GAP,杜绝所有的并发 更新/删除/插入 操作

16、MySQL DELETE 删除语句加锁分析
  http://www.fordba.com/lock-analyse-of-delete.html

17、别废话,各种SQL到底加了什么锁?
  https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961508&idx=1&sn=9f31a95e5b8ec16fa0edc7de6087d2a1&chksm=bd2d0d788a5a846e3bf16d300fb9723047bd109fd22682c39bdf7ed4e77b167e333460f6987c&scene=21#wechat_redirect
一、普通select:select ... from t;
(1)在读未提交(Read Uncommitted),读提交(Read Committed, RC),可重复读(Repeated Read, RR)这三种事务隔离级别下,普通select使用快照读(snpashot read),不加锁,并发非常高;
(2)在串行化(Serializable)这种事务的隔离级别下,普通select会升级为select ... in share mode;
二、加锁select
加锁select主要是指:
select ... for update
select ... in share mode
(1)如果,在唯一索引(unique index)上使用唯一的查询条件(unique search condition),会使用记录锁(record lock),而不会封锁记录之间的间隔,即不会使用间隙锁(gap lock)与临键锁(next-key lock);
例如:假设有InnoDB表:
t(id PK, name);
表中有三条记录:
1, shenjian
2, zhangsan
3, lisi
SQL语句:
select * from t where id=1 for update;
只会封锁记录,而不会封锁区间。
(2)其他的查询条件和索引条件,InnoDB会封锁被扫描的索引范围,并使用间隙锁与临键锁,避免索引范围区间插入记录;
三、update与delete
(1)和加锁select类似,如果在唯一索引上使用唯一的查询条件来update/delete,例如:
update t set name=xxx where id=1; 也只加记录锁;
(2)否则,符合查询条件的索引记录之前,都会加排他临键锁(exclusive next-key lock),来封锁索引记录与之前的区间;
(3)尤其需要特殊说明的是,如果update的是聚集索引(clustered index)记录,则对应的普通索引(secondary index)记录也会被隐式加锁,这是由InnoDB索引的实现机制决定的:普通索引存储PK的值,检索普通索引本质上要二次扫描聚集索引。
四、insert
同样是写操作,insert和update与delete不同,它会用排它锁封锁被插入的索引记录,而不会封锁记录之前的范围。
同时,会在插入区间加插入意向锁(insert intention lock),但这个并不会真正封锁区间,也不会阻止相同区间的不同KEY插入。


16、MySQL共享锁(读锁、S锁)与排他锁(写锁、X锁)
  https://blog.csdn.net/u014292162/article/details/83271299
共享锁(Share Locks,记为S锁),读取数据时加S锁
排他锁(eXclusive Locks,记为X锁),修改数据时加X锁
(1)事务拿到某一行记录的共享S锁,才可以读取这一行;
(2)事务拿到某一行记录的排它X锁,才可以修改或者删除这一行;
其兼容互斥表如下:
S X
S 兼容 互斥
X 互斥 互斥
即:
(1)多个事务可以拿到一把S锁,读读可以并行;
(2)而只有一个事务可以拿到X锁,写写/读写必须互斥;
共享锁与排他锁的玩法是:
共享锁之间不互斥,简记为:读读可以并行
排他锁与任何锁互斥,简记为:写读,写写不可以并行
可以看到,一旦写数据的任务没有完成,数据是不能被其他任务读取的,这对并发度有较大的影响。写事务没有提交,读相关数据的select也会被阻塞
由于数据加排他锁后,读相关数据也会被阻塞,因此并发性很低,由此引出了MVCC多版本并发控制:
数据多版本是一种能够进一步提高并发的方法,它的核心原理是:
(1)写任务发生时,将数据克隆一份,以版本号区分;
(2)写任务操作新克隆的数据,直至提交;
(3)并发读任务可以继续读取旧版本的数据,不至于阻塞;
MVCC就是通过“读取旧版本数据”来降低并发事务的锁冲突,提高任务的并发度。

17、mysql的mvcc(多版本并发控制)
  https://www.cnblogs.com/dongqingswt/p/3460440.html

18、SQL中的where条件,在数据库中提取与应用浅析
  http://hedengcheng.com/?p=577

19、mysql锁相关总结
MyISAM只支持表锁,InnoDB可以支持行锁。因此MyISAM:执行读写SQL语句时,会对表加锁,所以数据量大,并发量高时,性能会急剧下降。
而InnoDB:细粒度行锁,在数据量大,并发量高时,性能比较优异。
常见坑:
InnoDB的行锁是实现在索引上的,而不是锁在物理行记录上。因此,如果访问没有命中索引,也无法使用行锁,将要退化为表锁。
例如:
表:t_user(uid PK, uname, age, sex) 存储引擎为:innodb;其中uid为主键,无其他索引:
update t_user set age=10 where uid=1;
因为innodb会根据主键建立primary key,因此根据uid = 1查询命中索引,此时会会uid=1这条记录加行锁。
update t_user set age=10 where uid != 1;
如果表中没有uid!=1的记录,此时未命中索引,退化为表锁。
update t_user set age=10 where name=‘shenjian‘;
无索引,表锁。
启示:InnoDB务必建好索引,否则锁粒度较大,会影响并发。
总结:
行锁,对提高并发帮助很大
事务,对数据一致性帮助很大

MySQL默认的事务隔离级别是 Repeated Read (RR),假设使用的存储引擎是InnoDB,在这个隔离级别下:
(1)读取到数据,都是其他事务已提交的数据;---避免了脏读
(2)同一个事务中,相同的连续读,得到的结果应该是相同的; --- 避免了不可重复读
(3)不会出现insert幻象读; --- 避免了幻读
其中:不可重复读是针对同一条记录来说的,事务A两次读取同一条记录是一致的,不受事务B提交的影响
幻读是针对记录的行数来说的,事务A的两次范围读,数据的行数是一致的。
假设有数据表:
t(id int PK, name);
假设目前的记录是:
10, shenjian
20, zhangsan
30, lisi
Case 1
事务A先执行,并且处于未提交状态:
update t set name=’a’ where id=10; ---此时A会在id=10的记录上加X锁,其他事务的增删改操作都会阻塞
事务B后执行:
update t set name=’b’ where id=10;
因为事务A在PK id=10上加了行锁,因此事务B会阻塞。
Case 2
事务A先执行,并且处于未提交状态:
delete from t where id=40;
事务A想要删除一条不存在的记录。
事务B后执行:
insert into t values(40, ‘c’);
事务B想要插入一条主键不冲突的记录。
问题1:事务B是否阻塞?
会阻塞
问题2:如果事务B阻塞,锁如何加在一条不存在的记录上呢?
间隙锁的出现主要集中在同一个事务中先delete 后 insert的情况下,当我们通过一个参数去删除一条记录的时候,如果参数在数据库中存在,那么这个时候产生的是普通行锁,锁住这个记录,然后删除, 然后释放锁。
如果这条记录不存在,问题就来了, 数据库会扫描索引,发现这个记录不存在, 这个时候的delete语句获取到的就是一个间隙锁,然后数据库会向左扫描扫到第一个比给定参数小的值, 向右扫描扫描到第一个比给定参数大的值, 然后以此为界,构建一个区间, 锁住整个区间内的数据
问题3:事务的隔离级别,索引类型,是否对问题1和问题2有影响呢?
只有RR隔离级别才会有间隙锁

19、InnoDB共有七种类型的锁:
(1)共享/排它锁(Shared and Exclusive Locks) --- 见上面的介绍
(2)意向锁(Intention Locks)
意向锁是指,未来的某个时刻,事务可能要加共享/排它锁了,先提前声明一个意向。
意向锁有这样一些特点:
(1)首先,意向锁,是一个表级别的锁(table-level locking);
(2)意向锁分为:
意向共享锁(intention shared lock, IS),它预示着,事务有意向对表中的某些行加共享S锁
意向排它锁(intention exclusive lock, IX),它预示着,事务有意向对表中的某些行加排它X锁
举个例子:
select ... lock in share mode,要设置IS锁;
select ... for update,要设置IX锁;
(3)意向锁协议(intention locking protocol)并不复杂:
事务要获得某些行的S锁,必须先获得表的IS锁
事务要获得某些行的X锁,必须先获得表的IX锁
(4)由于意向锁仅仅表明意向,它其实是比较弱的锁,意向锁之间并不相互互斥,而是可以并行,其兼容互斥表如下:
IS IX
IS 兼容 兼容
IX 兼容 兼容
(5)额,既然意向锁之间都相互兼容,那其意义在哪里呢?它会与表级的共享锁/排它锁互斥,其兼容互斥表如下:
S X
IS 兼容 互斥
IX 互斥 互斥
意向锁的作用就是:
当一个事务在需要获取资源的锁定时,如果该资源已经被排他锁占用,则数据库会自动给该事务申请一个该表的意向锁。
如果自己需要一个共享锁定,就申请一个意向共享锁。如果需要的是某行(或者某些行)的排他锁定,则申请一个意向排他锁。
注:意向共享锁可以同时并存多个,但是意向排他锁同时只能有一个存在。
(6)插入意向锁(Insert Intention Locks)
---InnoDB并发插入,居然使用意向锁? https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961461&idx=1&sn=b73293c71d8718256e162be6240797ef&chksm=bd2d0da98a5a84bfe23f0327694dbda2f96677aa91fcfc1c8a5b96c8a6701bccf2995725899a&scene=21#wechat_redirect
插入意向锁,是间隙锁(Gap Locks)的一种(所以,也是实施在索引上的),它是专门针对insert操作的。
多个事务,在同一个索引,同一个范围区间插入记录时,如果插入的位置不冲突,不会阻塞彼此。
例如:在MySQL,InnoDB,RR下:
t(id unique PK, name);
数据表中有数据:
10, shenjian
20, zhangsan
30, lisi
事务A先执行,在10与20两条记录中插入了一行,还未提交:
insert into t values(11, xxx);
事务B后执行,也在10与20两条记录中插入了一行:
insert into t values(12, ooo);
(1)会使用什么锁?
(2)事务B会不会被阻塞呢?
回答:虽然事务隔离级别是RR,虽然是同一个索引,虽然是同一个区间,但插入的记录并不冲突,故这里:
使用的是插入意向锁
并不会阻塞事务B
(3)记录锁(Record Locks)
记录锁,它封锁索引记录,例如:
select * from t where id=1 for update;
它会在id=1的索引记录上加X锁,以阻止其他事务插入,更新,删除id=1的这一行。
需要说明的是:
select * from t where id=1;
则是快照读(SnapShot Read),它并不加锁
(4)间隙锁(Gap Locks)
间隙锁,它封锁索引记录中的间隔,或者第一条索引记录之前的范围,又或者最后一条索引记录之后的范围。
依然是上面的例子,InnoDB,RR:
t(id PK, name KEY, sex, flag);
表中有四条记录:
1, shenjian, m, A
3, zhangsan, m, A
5, lisi, m, A
9, wangwu, f, B
这个SQL语句
select * from t where id between 8 and 15 for update;
会封锁区间,以阻止其他事务id=10的记录插入。
画外音:
为什么要阻止id=10的记录插入?
如果能够插入成功,头一个事务执行相同的SQL语句,会发现结果集多出了一条记录,即幻影数据。
间隙锁的主要目的,就是为了防止其他事务在间隔中插入数据,以导致幻读。
如果把事务的隔离级别降级为读提交(Read Committed, RC),间隙锁则会自动失效。
(5)临键锁(Next-key Locks)
临键锁,是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间。
更具体的,临键锁会封锁索引记录本身,以及索引记录之前的区间。
如果一个会话占有了索引记录R的共享/排他锁,其他会话不能立刻在R之前的区间插入新的索引记录。
依然是上面的例子,InnoDB,RR:
t(id PK, name KEY, sex, flag);
表中有四条记录:
1, shenjian, m, A
3, zhangsan, m, A
5, lisi, m, A
9, wangwu, f, B
PK上潜在的临键锁为:
(-infinity, 1]
(1, 3]
(3, 5]
(5, 9]
(9, +infinity]
临键锁的主要目的,也是为了避免幻读(Phantom Read)。如果把事务的隔离级别降级为RC,临键锁则也会失效。
InnoDB,select为啥会阻塞insert? https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961471&idx=1&sn=da257b4f77ac464d5119b915b409ba9c&chksm=bd2d0da38a5a84b5fc1417667fe123f2fbd2d7610b89ace8e97e3b9f28b794ad147c1290ceea&scene=21#wechat_redirect
(7)自增锁(Auto-inc Locks) 插入InnoDB自增列,居然是表锁? https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961455&idx=1&sn=4c26a836cff889ff749a1756df010e0e&chksm=bd2d0db38a5a84a53db91e97c7be6295185abffa5d7d1e88fd6b8e1abb3716ee9748b88858e2&scene=21#wechat_redirect
自增锁是一种特殊的表级别锁(table-level lock),专门针对事务插入AUTO_INCREMENT类型的列。
最简单的情况,如果一个事务正在往表中插入记录,所有其他事务的插入必须等待,以便第一个事务插入的行,是连续的主键值。
例如:t(id AUTO_INCREMENT, name);,其中id为自增

20、意向锁的作用
例如下面的例子:
(1)事务A锁住了表中的一行,让这一行只能读,不能写,即行级S锁。
(2)之后,事务B申请整个表的写锁,及表级X锁。
如果没有意向锁,如果事务B申请成功,那么理论上它就能修改表中的任意一行,这与A持有的行锁是冲突的。
数据库需要避免这种冲突,就是说要让B的申请被阻塞,直到A释放了行锁。
数据库要怎么判断这个冲突呢?
step1:判断表是否已被其他事务用表锁锁表
step2:判断表中的每一行是否已被行锁锁住。
注意step2中通过遍历查询,这样的判断方法效率实在不高,因为需要遍历整个表。
于是就有了意向锁。在意向锁存在的情况下,事务A必须先申请表的意向共享锁IS,成功后再申请一行的行锁X。
在意向锁存在的情况下,上面的判断可以改成
step1:不变
step2:发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。
最终结论:
(1)申请意向锁的动作是数据库完成的,就是说,事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁,不需要我们程序员使用代码来申请。
(2)IX,IS是表级锁,不会和行级的X,S锁发生冲突。只会和表级的X,S发生冲突。行级别的X和S按照普通的共享、排他规则即可。
所以之前的示例中第2步不会冲突,只要写操作不是同一行,就不会发生冲突。

21、Innodb快照读(普通查询:select ... from t)和加锁读(select ... from t for update)的区别
在快照读(Snapshot Read)情况下,MySQL数据库,InnoDB存储引擎,为了提高并发,使用MVCC机制,
在并发事务时,通过读取数据行的历史数据版本,不加锁,来提高并发的一种不加锁一致性读(Consistent Nonlocking Read)。
在读提交(RC),可重复读(RR)两个不同的事务的隔离级别下,快照读和加锁读都有什么不同呢?
在RC级别下:
快照读通过MVCC机制保证没有脏读,即在事务A在读取数据时,发现事务B修改了数据但是没有提交,事务A根据undo log获取B修改前的数据。
加锁读通过加行记录X锁保证其他事务的修改操作会被阻塞。
在RR级别下:
快照读通过MVCC机制保证没有脏读,即在事务A在读取数据时,发现事务B修改了数据但是没有提交,事务A根据undo log获取B修改前的数据。
快照读通过MVCC机制保证可重复读,即在事务A在读取数据时,会给改行记录增加一个版本号,如果事务B修改了数据并提交会修改该版本号,事务A再一次读取时发现版本号被修改了,事务A根据undo log获取B修改前的数据。
加锁读通过加行记录X锁保证其他事务的修改操作会被阻塞。

事务总能够读取到,自己写入(update /insert /delete)的行记录
RC下,快照读总是能读到最新的行数据快照,当然,必须是已提交事务写入的
RR下,某个事务首次read记录的时间为T,未来不会读取到T时间之后已提交事务写入的记录,以保证连续相同的read读到相同的结果集
例如表: t(id PK, name);其中id为主键。
表中有三条记录:
1, shenjian
2, zhangsan
3, lisi
case 1,两个并发事务A,B执行的时间序列如下(A先于B开始,B先于A结束):
A1: start transaction;
B1: start transaction;
A2: select * from t;
B2: insert into t values (4, wangwu);
A3: select * from t;
B3: commit;
A4: select * from t;
提问1:假设事务的隔离级别是可重复读RR,事务A中的三次查询,A2, A3, A4分别读到什么结果集?
回答:RR下
(1)A2读到的结果集肯定是{1, 2, 3},这是事务A的第一个read,假设为时间T;
(2)A3读到的结果集也是{1, 2, 3},因为B还没有提交;
(3)A4读到的结果集还是{1, 2, 3},因为事务B是在时间T之后提交的,A4得读到和A2一样的记录;
提问2:假设事务的隔离级别是读提交RC,A2, A3, A4又分别读到什么结果集呢?
回答:RC下
(1)A2读到的结果集是{1, 2, 3};
(2)A3读到的结果集也是{1, 2, 3},因为B还没有提交;
(3)A4读到的结果集是{1, 2, 3, 4},因为事务B已经提交;
case 2,仍然是上面的两个事务,只是A和B开始时间稍有不同(B先于A开始,B先于A结束):
B1: start transaction;
A1: start transaction;
A2: select * from t;
B2: insert into t values (4, wangwu);
A3: select * from t;
B3: commit;
A4: select * from t;
提问3:假设事务的隔离级别是可重复读RR,事务A中的三次查询,A2, A3, A4分别读到什么结果集?
提问4:假设事务的隔离级别是读提交RC,A2, A3, A4的结果集又是什么呢?
回答:事务的开始时间不一样,不会影响“快照读”的结果,所以结果集和case 1一样。
case 3,仍然是并发的事务A与B(A先于B开始,B先于A结束):
A1: start transaction;
B1: start transaction;
B2: insert into t values (4, wangwu);
B3: commit;
A2: select * from t;
提问5:假设事务的隔离级别是可重复读RR,事务A中的A2查询,结果集是什么?
提问6:假设事务的隔离级别是读提交RC,A2的结果集又是什么呢?
回答:在RR下,
A2是事务A的第一个read,假设为时间T,它能读取到T之前提交事务写入的数据行,故结果集为{1, 2, 3, 4}。在RC下,没有疑问,一定是{1, 2, 3, 4}。
case 4,事务开始的时间再换一下(B先于A开始,B先于A结束):
B1: start transaction;
A1: start transaction;
B2: insert into t values (4, wangwu);
B3: commit;
A2: select * from t;
提问7:假设事务的隔离级别是可重复读RR,事务A中的A2查询,结果集是什么?
提问8:假设事务的隔离级别是读提交RC,A2的结果集又是什么呢?
回答:事务的开始时间不一样,不会影响“快照读”的结果,所以结果集和case 3一样。

22、超赞,InnoDB调试死锁的方法!
  https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961535&idx=1&sn=b62e9d71836ac5cf2d3cedf69e1ef395&chksm=bd2d0d638a5a84750adfc39d7e177a63330d6bde0f56600764b2d79e0fb9d96ad69e26e19ff1&scene=21#wechat_redirect

23、当执行一条select语句时,MySQL到底做了啥?
  https://mp.weixin.qq.com/s?__biz=MjM5NjMyMjUzNg==&mid=2448131610&idx=1&sn=1938ca532efe902758b51c3eee33d30b&chksm=b2f42b9d8583a28b410ff846059e878e8a46ba05cdc1199eef0b88de78d4c216fe689b8c85f6&mpshare=1&scene=1&srcid=1123m90BQpXy9p8GgMXaiMlB#rd
(1)建立连接:客户端通过连接器连接到数据库,包括建立连接、获取用户权限、维持连接、管理连接,连接完成后,若没有任何操作,连接就处于休眠状态,用命令 show processlist;查看,就是 Sleep 状态的进程。
连接器不会让你一直握着连接不动,若休眠时间超过 wait_timeout(默认为 8 小时),则会断开当前连接。
(2)查询缓存:在解析一个查询语句之前,如果查询缓存是打开的,那么MySQL会优先检查这个查询是否命中查询缓存中的数据。
如果当前的查询恰好命中了查询缓存,那么在返回查询结果之前MySQL会检查一次用户权限。若权限没有问题,MySQL会跳过所有其他阶段(解析、优化、执行等),直接从缓存中拿到结果并返回给客户端。
MySQL查询缓存的内容为select的结果集,在内存中是以HASH结构来进行映射。
*** cache会使用完整的sql字符串做key,并区分大小写,空格等。即两个sql必须完全一致才会导致cache命中。
缓存失效:a) 一旦表数据进行任何一行的修改,基于该表相关 cache 立即全部失效,并且从缓冲区中移出;
b) 为什么不做聪明一点判断修改的是否 cache 的内容?因为分析 cache 内容太复杂,服务器需要追求最大的性能。
(3)解析:词法分析的作用是将整个查询分解为多个元素。MySQL 从你输入的 select 这个关键字识别出来,这是一个查询语句。它也要把字符串 T 识别成一个表名,把字符串 ID 识别成一个列。
根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。如果你的语句不对,就会收到 You have an error in your SQL syntax 的错误提醒。
(4)优化: MySQL 查询优化器最主要的目标是尽可能地使用索引,并且使用最严格的索引来消除尽可能多的数据行。
MySQL 的优化器有几个重要任务: 1、选择最合适的索引; 2、选择表扫还是走索引; 3、选择表关联顺序; 4、优化 where 子句; 5、排除管理中无用表; 6、决定 order by 和 group by 是否走索引;
7、尝试使用 inner join 替换 outer join;8、简化子查询,决定结果缓存;9、合并试图;
实例1: SELECT col3 FROM mytable WHERE col1 = ‘value1‘ AND col2 = ‘value2‘;
假设col1上的测试匹配了900个数据行,col2上的测试匹配了300个数据行,而同时进行的测试只有30个数据行。
先测试col1会有900个数据行,需要检查它们找到其中的30个与col2中的值匹配记录,其中就有870次是失败了。
先测试col2会有300个数据行,需要检查它们找到其中的30个与col1中的值匹配的记录,只有270次是失败的,因此需要的计算和磁盘 I/O 更少。
其结果是,优化器会先测试 col2,因为这样做开销更小。
实例2: WHERE mycol < 4 / 2
WHERE mycol * 2 < 4
对于第一行,优化器把表达式 4/2 简化为 2,接着使用 mycol 上的索引来快速地查找小于 2 的值。
对于第二个表达式,MySQL 必须检索出每个数据行的 mycol 值,乘以 2,接着把结果与 4 进行比较。在这种情况下,不会使用索引。数据列中的每个值都必须被检索到,这样才能计算出比较表达式左边的值。
(5)执行:执行开始之前,会先判断是否有操作权限,若没有,会抛出相关异常。
如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。


































































































数据库笔记

原文:https://www.cnblogs.com/aiqiqi/p/10509591.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!