日常提交

This commit is contained in:
wu xiangkai
2022-09-23 19:38:10 +08:00
parent beb4c03f61
commit 157744bda1
6 changed files with 218 additions and 143 deletions

View File

@@ -0,0 +1,6 @@
# binlog 日志
* ## binlog二进制日志
* 定义binlog日志文件也称之为变更日志文件记录了数据库记录的所有DDL、DML等数据库更新事件的语句但是不包括任何没有更新数据的语句。
* 用途:
* 数据恢复如果mysql数据库进程意外停止可以通过binlog中的记录来恢复数据库中的数据
* 数据复制可以通过binlog来实现mysql数据库的主从一致

49
mysql/mysql底层/mvcc.md Normal file
View File

@@ -0,0 +1,49 @@
# 多版本并发控制
* ## 多版本并发控制解决的问题:
* 在多个事务并发访问资源时,可能会发生并发安全问题。为了解决多个事务同时对资进行读写的问题,可以采用如下解决方案:
* 多事务同时对资源进行读取:并发安全
* 多事务同时对资源进行写操作:非并发安全,可以对写操作进行加锁来解决并发安全问题
* 多事务同时对资源进行读和写操作:非并发安全
1. 对读操作和写操作都加锁:性能较差
2. 通过MVCC机制来解决并发读写的问题对写操作加锁并用MVCC机制来使读操作读到先前的值
* ## 多版本并发控的概念:
* MVCC通过数据库行的多个版本管理来实现数据库的并发控制。当一个写事务正在更新数据行的值时通过MVCC机制可以在另一个事物对数据行进行读操作时读取到被写事务更新之前的值。
* 通过MVCC机制可以更好的解决事务在读-写操作同时发生时的性能问题。MVCC可以避免在写事务时另一个读事务必须等待当前写事务释放排他锁而是可以通过MVCC读取资源被写事务修改之前的值。
* ## mysql中读操作的种类
* 当前读:读取数据库中当前的值,为了解决并发安全问题,需要对读操作进行加锁操作,可以尝试加排他锁或者共享锁,当当前事务对资源进行写操作时,读事务会阻塞直到写事务释放锁:
```mysql
# 为select语句加上共享锁
select * from user where... lock in share mode
# 为select语句加上排他锁
select * from user where... for update
```
* 快照读mysql中默认select语句的读取方式当当前事务对资源进行读操作时如果另一个事务正在对资源进行写操作那么读操作并不会阻塞而是会读取资源被写事务修改之前的值
* ## MVCC原理
* 隐藏字段对于使用innodb存储引擎的表其聚簇索引包含两个隐藏字段
1. trx_id每个事务对每条记录进行更改时该trx_id字段都会记载最后一次修改该行记录的事务id
2. roll_pointer指向undo log中关于修改该条数据的记录
* ReadViewReadView是MVCC机制中事务对数据进行快照读时产生的一个读视图。
* ReadView原理
* 当事务开启时会产生当前数据库系统的一个快照innodb为每个事务构造了一个数组用来维护当前系统中活跃事务的ID活跃事务为当前开启了但是尚未提交的事务id
* ReadView中保存了如下信息
* creator_trx_id创建当前ReadView的事务id
* trx_ids记录创建失误时当前mysql系统中活跃的事务id集合(已经开始但是还没有被提交的事务id)
* up_limit_idtrx_ids中最小的事务id
* low_limit_id表示生成ReadView中当前系统应该分配给下一个事务的id
* MVCC仅针对读已提交和可重复读的情况在读已提交的情况下事务中每执行一次select操作ReadView都会重复生成而在可重复读的隔离级别下事务仅仅会在第一次select操作时生成ReadView
* MVCC细节
* 当select语句想要对一条记录中的数据进行读取时首先会查看记录的trx_id是是否对当前事务的读操作是可见的判断事务是否可见的标准如下所示
* 如果记录的trx_id大于low_limit_id那么说明在创建ReadView时对记录进行修改的事务还没有被创建当然修改对当前读事务来说是不可见的
* 如果trx_id小于up_limit_id那么说明对该记录进行修改的事务id小于创建ReadView时最小的活跃事务id在创建ReadView时修改记录的事物已经被提交修改对当前事务来说可见
* 如果trx_id位于up_limit_id和low_limit_id之间那么
* trx_id如果与trx_ids中保存的某个活跃事务id相同那么说明在创建ReadView时修改事务的id尚未被提交修改对当前失误不可见
* 如果trx_id与trx_ids中每个活跃事物id都不相同那么修改事务在创建ReadView事物之时已经被提交修改对当前事务可见
* 根据上述规则如果想要读取的记录trx_id对当前事务来说可见那么获取该事务id所对应的数据值如果trx_id对当前ReadView来说不可见那么沿着roll_pointer沿着undo log向前寻找知道找到对当前ReadView可见的事务id如果undo log中所有的事务id对当前ReadView来说都不可见那么对当前事物来说数据表中该条记录并不可见
* ## MVCC与幻读问题
* MVCC仅在读已提交和可重复读的情况下起作用而关于幻读问题MVCC仅在可重复读的隔离级别下解决。
* 在可重复读的隔离级别下,ReadView仅仅在第一次select语句时生成故而在同一事务中多次读取之间其他事务插入了新数据那么该新数据对应的trx_id在readView创建时应该处于活跃或者未创建的状态故而对ReadView所对应事物来说即使其他事务插入了新数据那么新插入的数据也不可见。
* 而在读已提交的隔离级别下ReadView在每次读操作的情况下都会被创建故而在两次读操作之间如果新的数据被插入那么新插入的数据对后一次读操作创建的ReadView来说是已经提交的数据是可见的。故而MVCC在读已提交的隔离级别下并不能够解决幻读的问题。

View File

@@ -0,0 +1,64 @@
# mysql锁
* ## msyql中锁的分类
* 从数据库操作的类型划分mysql中的锁可以分为读锁/共享锁和写锁/排他锁
* 读锁S锁Share
* 写锁X锁Exclusive
* 写锁和读锁示例
```mysql
# 为语句加上写锁
select * from table_name for udpate
# 为查询语句加上读锁
select * from table_name lock in share mode
# or
select * from table_name for share(mysql 8.0新增语法)
```
* 在获取读锁或者写锁之后只有当事务结束之后获取到的读锁或者写锁才会被释放。在innodb中for update或者for share语句只会为查询匹配的行数据上锁并且当事务结束或者回滚之后会释放为数据行所加的排他锁或者共享锁
* 在对mysql中数据进行写操作UPDATE、INSERT、DELETE)时,会通过如下方式加锁:
* DELETE操作对mysql表中数据执行DELETE操作需要为要删除数据添加排他锁X锁
* UPDATE操作对mysql表中数据执行UPDATE操作分如下情况
* update操作不改变记录的键值且更新的列在更新前后占用的空间未发生变化
* 此时会在B+数中定位到该条记录并且为该条记录添加X锁在修改完成之后释放X锁
* update操作没有修改键值但是更新前后该条记录的某一列占用空间发生了变化
* 此时会先对原先的记录加上X锁并进行DELETE操作并且在DELETE操作后通过INSERT操作并添加隐式锁来插入修改后的数据
* update操作修改了键值
* 同样也会通过先DELETE再INSERT的操作来更新数据
* INSERT操作通过添加隐式锁的操作来保证mysql中多事务的并发安全
* 从mysql数据库的粒度来对锁进行划分
* 表锁:
* 表级锁会锁住mysql中的整张表其粒度较大受其影响并发性能较低
* 表级锁是mysql中最基本的加锁策略并不依赖于存储引擎不管是innodb还是myisam都支持表级锁
* 表级锁的使用场景:
* 通过的select或者update、delete、insert操作innodb并不会加表级锁但是对于alter table、drop table这类ddl语句对表的结构进行修改时需要对其表的元数据进行加锁MDL对元数据加锁的过程中想要对该表中数据进行select、delete、update、insert的操作会被阻塞
* 对表锁进行加锁的语句:
```mysql
# 对表锁进行加锁
# 在对表进行上锁操作后,无法再去读取其他未上锁的表
lock tables table_name read/write
# 对上锁的表进行释放操作
unlock tables;
```
* 对于myisam存储引擎读操作之前会为涉及到的表加上读锁并且在读操作执行完成之后释放上锁的表而对于写操作myisam存储引擎会在写操作执行之前为涉及到的表加上写锁。反之innodb存储引擎在对数据进行查找和修改时不会在表级别加锁而是会在更细的粒度上为行数据进行加锁以此来提高程序的并发性能。
* 元数据锁MDL对于表结构的修改DDL操作会对其元数据锁进行加锁而对于该表的增删改操作则是会默认加上元数据的读锁。故而在对表结构进行修改时想要对表中数据进行増删改操作需要获取其元数据的读锁此时对表数据的増删改操作会被阻塞。
* 意向锁意向锁通常用来简化行级锁和表级锁是否兼容的判断操作。如果为一个表中某条数据加上行级锁那么innodb存储引擎会自动的为该行记录所在的页和表加上页级和表级的意向锁那么当接下来有事务想要对该表进行表级加锁操作时就无需查看该表中所有数据来判断是否存在表中数据的锁和表级锁冲突。只需判断该表的意向锁和想要加上的表级锁是否冲突即可。
* 行锁
* 相对与表锁,行锁的粒度更小,故而其并发度更高,并发性能更好。但是行锁可能会造成死锁问题,加锁的开销也更大。
* 是否支持行锁取决与存储引擎比如myisam不支持行锁但是innodb却支持行锁
* 行锁种类:
* 记录锁记录锁针对于单条记录在一个事物中如果存在update、delete等修改操作其默认会获得想要修改的那条记录的记录锁并且在执行修改操作之后才会被释放。若一个事务中对某条记录调用update操作那么直到当前事务提交或者回滚前若其他事务想要修改同一行记录会进入阻塞状态知道持有记录写锁的退出才能继续执行。
* 间隙锁间隙锁对记录的范围进行加锁。间隙锁是不冲突的多个事务可以同时持有某一个范围的间隙锁。用间隙锁或者mvcc机制都能够解决死锁问题。但是间隙锁可能会导致死锁问题如果多个事务各自持有自己范围的间隙锁并同时向对方持有间隙锁的范围插入数据此时两个事务都会等待对方释放间隙锁在这种情况下会发生死锁问题。
* 临键锁next-key lock在锁住某条记录的同时也锁定某个范围内的数据使之无法被其他事务插入数据
* 页锁:在页面的粒度上进行上锁
* 在粒度上不同层级的所的数量是有上限的例如innodb其优先会选用行锁来进行加锁。当行锁数量过多占用空间超过表空间上限时会进行锁升级的行为。行锁会升级为页面锁此时锁的粒度会增加锁花费的开销也会变小但是粒度变大后并发性能也会变低。
* 通过对待锁的方式进行划分
* 乐观锁:乐观锁假设每次对数据进行修改时,其他事务不会访问数据,故而不会对数据正真的加锁,只是会在修改时检查数据是否被其他事务修改过。
* 乐观锁的实现方式:
* cas
* 版本号机制
* 悲观锁:
* 悲观锁假设一个事务在对数据进行修改时其他事务也会对数据进行修改故而在每次修改数据时都会对要修改的数据进行加锁悲观锁是通过mysql内部提供的锁机制来实现的
* 通过加锁方式进行划分:
* 隐式锁隐式锁通常用于插入操作。在某一个事务在对所记录进行插入操作时如果其他事务想要访问该条记录会查看最后修改该条记录的事务id是否是活跃的事务如果是活跃事务那么其会帮助插入事务创建一个X锁并且让自己进入阻塞状态。
* 隐式锁是一种延迟加锁的机制,只有当有其他事务想要访问未提交事务插入的记录时,隐式锁才会被创建。该机制能够有效减少创建锁的数量。
* 显示锁可以通过命令查看到的锁通常会用for update、lock in share mode 或者 update、delete操作会生成显示锁

View File

@@ -0,0 +1,5 @@
# redo log & undo log
* ## undo log
* undo log通常用于事务的回滚操作用来保证事务的原子性。
* 每当发生数据修改操作时update、insert、delete关于当前修改操作的相反操作会被记录到undo log中通常用于在回滚时将数据回复到修改之前的值。
* undo log默认被记录到mysql的表空间中因而对undo log进行追加时对表中页数据进行修改时也会产生redo log对undo log的追加会通过fsync操作持久化到redo log中。这样即使在一个事务尚未被提交或是回滚之前mysql服务器崩溃下次重启时也可以通过redo log恢复对数据的修改和undo log的内容回滚事务时也能将数据恢复到崩溃前尚未被事务修改的状态。

View File

@@ -0,0 +1,24 @@
# mysql索引
* ## mysql索引的分类
* 从功能逻辑上对索引进行分类:
* 普通索引:只是用于提升查询效率,没有任何的附加约束
* 唯一性索引通过unique关键字可以设定唯一性索引其会限制该索引值必须是唯一的但是允许为null
* 主键索引:特殊的唯一性索引,在唯一性索引的基础上,主键索引还增加了不为空的约束
* 单列索引:作用在一个字段上的索引
* 联合索引:作用于多个字段上的索引
* ## 索引的创建、删除操作
```mysql
# 索引的创建方式
alter table table_name add [unique/index] idx_name (col_name...)
# or
create [unique/index] on table_name(col_name...)
# 索引的删除方式
alter table table_name drop index idx_name
# or
drop index idx_name on table_name
```
* ## 索引的可见性
```mysql
# 通过修改索引的可见性,可以比较创建
```