读公众号《架构师之路》笔记 ---- MySql

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010588262/article/details/82290757

MySql的两大知名引擎:MyISAM 和 InnoDB

InnoDB的两大优势: 事务 行锁(基于索引,与Oracle行锁不同)


并发控制的两种手段:

1. 锁

普通锁:所有行为都加一样的锁,串行执行,效率极低
共享锁(S锁,Share)排它锁(X锁,eXclude)
读数据使用共享锁,共享锁与共享锁不互斥,写数据使用排它锁,排它锁与所有锁互斥
导致的结果是,读读并行,读写、写写不并行

2. 数据多版本

涉及到原版数据修改中的数据
当修改数据时,把原版数据拷贝到一个地方做改动,也就是修改中的数据
读任务永远去读原版数据,做到读写并行

重点

  1. 普通锁:完全不并行
  2. 共享锁排它锁:读读并行
  3. 数据多版本:读写并行

InnoDB引擎使用的就是数据多版本,实现高并发

具体细节:

1. 事务提交时,不立即将数据刷到磁盘,而是写redo日志
数据刷到磁盘 - 随机写
redo日志 - 顺序写

扩展阅读:https://blog.csdn.net/u010087886/article/details/54405934

顺序写的效率远高于随机写,数据库会定期将redo日志里的内容更新到磁盘上,这样做可以极大提高效率,假如某个时间点,数据库突然宕机或被关闭了,再次启动时,redo日志就会像它的名字一样被再次执行以确保回到关闭前的状态

这里加上一些个人猜测的基础理解吧,数据肯定是要存磁盘的,但是启动的时候也会将一些表数据放在内存(并非查询缓存),对应着innodb_buffer_pool_size参数设置的区域,操作数据时可以直接改内存里的,并且记录redo日志即可,反正查询也是先查内存,是最新数据,mysql定期将redo日志刷到磁盘(可以自己配置频率)

2. 事务执行时,将对数据的增删改操作存入undo日志
undo日志存在回滚段中,当事务rollback的时候,就依靠回滚段中的undo日志将数据库恢复到事务执行前的状态

InnoDB引擎会给每行数据加三个内部属性:
1. 最近一次修改它的事务ID
2. 指向undo日志的指针
3. 单调递增的行ID


InnoDB的各种锁:

隔离级别:默认RR

1. 自增锁:表锁

  • 事务A插入一条数据自增ID是1,没提交
  • 假如不加表锁,事务B此时也插入一条数据,ID自增为2,没提交
  • 事务A再插一条数据,ID是3
  • 事务A查询数据,查到ID是1和3,就会觉得很奇怪,连续插的两条数据怎么ID隔了一个
  • 所以要加表锁哟

2. 意向锁

意向锁是表锁,意向锁与意向锁之间是不冲突的
意向锁的作用比较难说清楚,这篇说的详细并且清楚:

扩展阅读:https://blog.csdn.net/arkblue/article/details/53895150
1. mysql中的表锁:
LOCK TABLE my_tabl_name READ; 用读锁锁表,会阻塞其他事务修改表数据。
LOCK TABLE my_table_name WRITe; 用写锁锁表,会阻塞其他事务读和写。
2.Innodb引擎支持行锁,行锁分为
共享锁,一个事务对一行的共享只读锁。
排它锁,一个事务对一行的排他读写锁。
3.这两中类型的锁共存的问题
考虑这个例子:
事务A锁住了表中的一行,让这一行只能读,不能写。
之后,事务B申请整个表的写锁。
如果事务B申请成功,那么理论上它就能修改表中的任意一行,这与A持有的行锁是冲突的。
数据库需要避免这种冲突,就是说要让B的申请被阻塞,直到A释放了行锁。
数据库要怎么判断这个冲突呢?
step1:判断表是否已被其他事务用表锁锁表
step2:判断表中的每一行是否已被行锁锁住。
注意step2,这样的判断方法效率实在不高,因为需要遍历整个表。
于是就有了意向锁。
在意向锁存在的情况下,事务A必须先申请表的意向共享锁,成功后再申请一行的行锁。
在意向锁存在的情况下,上面的判断可以改成
step1:不变
step2:发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。
注意:申请意向锁的动作是数据库完成的,就是说,事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁,不需要我们程序员使用代码来申请。

3. 插入意向锁

行锁
这种锁要与自增锁区分开来,这种锁针对不带自增列的表
这种锁还要与意向锁区分开来,这种锁针对数据插入,而不是更新或删除,而且是行锁不是表锁

4. 记录锁(行锁)

就是锁住某一条记录
select * from t where id=1 for update;
update t set name='hugeo' where id = 1
都会在id=1的索引记录上加锁,以阻止其他事务插入,更新,删除id=1的这一行。
加记录锁的时候会自动先给表加上意向锁。

5. 间隙锁

锁住间隙,例如:
select * from t where id between 8 and 15 for update
当事务A锁住8到15的范围之后,其他事务不能插入8-15之间的数据,否则如果事务A再次查询就会跟第一次查询结果不一致的,也就不满足不可重复读了,所以当隔离级别低于RR的时候,例如ReadCommit,间隙锁就会失效
间隙锁本身是只锁间隙不锁已存在的数据本身的,只锁间隙意味着只限制insert语句,因为你无法删改不存在的数据
有个暂时不理解的问题,当只对一条记录加锁的时候也会锁间隙,不知道为什么,例如下表:
这里写图片描述
id是主键,number没加索引
当事务A执行:select * from gaptest where number=4 for update;还未提交
事务B执行:insert into gaptest value(2,2);就会阻塞
这是因为事务A将(1, 2)(3, 4)以及(3, 4)(6, 5)之间都用间隙锁锁住了,(2, 2)就无法插入了,但是不明白为什么,因为插入成功也不会导致不可重复读啊

6. 临键锁(next-key锁)

其实就是记录锁+间隙锁,即锁定一个范围,并且锁定记录本身,InnoDB默认加锁方式是next-key锁。


MySQL索引,B+树

聚集索引:

  1. 每张表只能有一个聚集索引。主键->第一个NonUnique列->InnoDB自己创建的隐藏的RowID列
  2. B+树的叶子节点直接存储行数据

非聚集索引

  1. 普通索引
  2. B+树的叶子节点存PK值,所以通过非聚集索引找数据要扫描2次

猜你喜欢

转载自blog.csdn.net/u010588262/article/details/82290757
今日推荐