数据库学习

日志中的Redo Log和BinLog

参考:https://www.jianshu.com/p/4bcfffb27ed5
mysql通过WAL(write-ahead logging)技术保证事务

在同一个事务中,每当数据库进行修改数据操作时,将修改结果更新到内存后,会在redo log添加一行记录记录“需要在哪个数据页上做什么修改”,并将该记录状态置为prepare,等到commit提交事务后,会将此次事务中在redo log添加的记录的状态都置为commit状态,之后将修改落盘时,会将redo log中状态为commit的记录的修改都写入磁盘。有了redo log,当数据库发生宕机重启后,可通过redo log将未落盘的数据恢复,即保证已经提交的事务记录不会丢失

redo log是存储引擎层(InnoDB)的,而binlog是数据库server层的

为什么仍然需要binlog

1、redo log的大小是固定的,日志上的记录修改落盘后,日志会被覆盖掉,无法用于数据回滚/数据恢复等操作。

2、redo log是innodb引擎层实现的,并不是所有引擎都有。

两阶段提交

考虑非两阶段提交的情况:

1.先写redo log后写binlog。假设在redo log写完,binlog还没有写完的时候,MySQL进程异常重启。redo log写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行c的值是1。但是由于binlog没写完就crash了,这时候binlog里面就没有记录这个语句。因此,之后备份日志的时候,存起来的binlog里面就没有这条语句。如果需要用这个binlog来恢复临时库的话,由于这个语句的binlog丢失,这个临时库就会少了这一次更新,恢复出来的这一行c的值就是0,与原库的值不同。

2.先写binlog后写redo log。如果在binlog写完之后crash,由于redo log还没写,崩溃恢复以后这个事务无效,所以这一行c的值是0。但是binlog里面已经记录了“把c从0改成1”这个日志。所以,在之后用binlog来恢复的时候就多了一个事务出来,恢复出来的这一行c的值就是1,与原库的值不同。

两阶段提交:保证了redo log和binlog的一致性

1 prepare阶段  2 写binlog   3 commit

当在2之前崩溃时:重启恢复:发现没有commit,回滚。备份恢复:没有binlog 。此情况下一致

当在3之前崩溃:重启恢复:虽没有commit,但满足prepare和binlog完整,所以重启后会自动commit。备份:有binlog. 一致

注意一下:可能在binlog写过程crash,重启后binlog不完整,会回滚。只要binlog完整,就会自动commit


两阶段提交

1、innodb_flush_log_at_trx_commit:设置为1,表示每次事务的redolog都直接持久化到磁盘(注意是这里指的是redolog日志本身落盘),保证mysql重启后数据不丢失。

2、sync_binlog: 设置为1,表示每次事务的binlog都直接持久化到磁盘(注意是这里指的是binlog日志本身落盘),保证mysql重启后binlog记录是完整的。

二者区别

参考:https://blog.csdn.net/wanbin6470398/article/details/81941586

1.redo log是在InnoDB存储引擎层产生,而binlog是MySQL数据库的上层产生的,binlog不像redo log只支持InnoDB,它支持很多存储引擎。

2.MySQL的binlog是逻辑日志,statement模式下记录的SQL语句,row模式下记录更新前和更新后的两条行内容。而innodb存储引擎层面的重做日志是物理日志,记录修改后的值。

3.binlog不是循环使用,在写满或者重启之后,会生成新的binlog文件,redo log是循环使用

4.binlog可以作为恢复数据使用,主从复制搭建,redo log作为异常宕机或者介质故障后的数据恢复使用。


索引

InnoDB的索引结构

Innodb存储引擎中行记录就是按照聚集索引维度顺序存储的,Innodb的表也称为索引组织表。聚集索引一般是表中的主键索引,如果表中没有显示指定主键,则会选择表中的第一个不允许为NULL的唯一索引,如果还是没有的话,就采用Innodb存储引擎为每行数据内置的6字节ROWID作为聚集索引。聚集索引的索引节点为数据页

而普通索引为非聚集索引。其索引节点存储主键索引。使用普通索引查询的过程:通过普通索引查到主键索引值,再从主键索引(聚集索引)中查到对应主键索引值的数据。回到主键索引树搜索的过程,我们称为回表

ID为主键索引,k为普通索引

select * from T where k between 3 and 5 的过程:

1.在k索引树上找到k=3的记录,取得 ID = 300;

2.再到ID索引树查到ID=300对应的R3;

3.在k索引树取下一个值k=5,取得ID=500;

4.再回到ID索引树查到ID=500对应的R4;

5.在k索引树取下一个值k=6,不满足条件,循环结束。


索引覆盖

使用索引查询时,索引覆盖了查询需求,就是索引覆盖。减少了IO

(1)以普通索引为例,若我们想要的就是主键值,如上所示,普通索引的索引节点存储的正是主键索引,就不用回表查主键索引。

(2)以联合索引为例,满足最左原则条件下,我们想要的正是联合索引中的字段,也不用回表。


若想根据名字查年龄,直接通过联合索引查到结果,不用回表

数据库锁

全局锁

Flush tables with read lock (FTWRL),整个库处于只读状态。

全局锁的典型使用场景是,做全库逻辑备份。

缺点:如果你在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆

          如果你在从库上备份,那么备份期间从库不能执行主库同步过来的binlog,会导致主从延迟

备份场景:可以使用  mysqldump使用参数–single-transaction。导数据之前就会启动一个事务,来确保拿到一致性视图。而由于MVCC的支持,这个过程中数据是可以正常更新的。只能应用于支持事务的存储引擎。

表级锁

表锁

lock tables(tablename) read/write。除了会限制别的线程的读写外,也限定了本线程接下来的操作对象。举个例子, 如果在某个线程A中执行lock tables t1 read, t2 write; 这个语句,则其他线程写t1、读写t2的语句都会被阻塞。同时,线程A在执行unlock tables之前,也只能执行读t1、读写t2的操作。连写t1都不允许,自然也不能访问其他表

元数据锁

MDL(metadata lock) 保证DDL和DML的一致性。其实就是针对表结构的锁

当对一个表做增删改查操作的时候,加MDL读锁;当要对表做结构变更操作的时候,加MDL写锁:读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查;读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。因此,如果有两个线程要同时给一个表加字段,其中一个要等另一个执行完才能开始执行。

MDL会直到事务提交才释放

行锁

在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。InnoDB的行级锁是通过锁索引记录实现的。如果条件没有索引那么会锁表。

如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁的申请时机尽量往后放。

行锁会产生死锁

事务A等待id=2行锁释放,事务B等待id=1行锁释放

死锁解决方案

(1)直接进入等待,直到超时。这个超时时间可以通过参数innodb_lock_wait_timeout来设置。默认50s,对于在线服务来说,这个等待时间往往是无法接受的。但是如果设为较短的时间,对于稍长一点的锁等待会产生误伤。

(2)发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数innodb_deadlock_detect设置为on,表示开启这个逻辑。但是死锁检测也是需要额外负担的。开启后并不是所有事务都要进行死锁检测,只有要加锁访问的行上有锁才检测。

MVCC

注意:虽然低水位是视图数组里的最小值,但是高水位并不是视图数组里的最大值。


低水位以下都已提交。中间黄色部分未必都是未提交,可能部分提交,在活跃数组中的就是未提交。高水位以后的都是未来的事务,未提交。

你可能感兴趣的:(数据库学习)