日志中的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作为聚集索引。聚集索引的索引节点为数据页
而普通索引为非聚集索引。其索引节点存储主键索引。使用普通索引查询的过程:通过普通索引查到主键索引值,再从主键索引(聚集索引)中查到对应主键索引值的数据。回到主键索引树搜索的过程,我们称为回表
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的行级锁是通过锁索引记录实现的。如果条件没有索引那么会锁表。
如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁的申请时机尽量往后放。
行锁会产生死锁
死锁解决方案:
(1)直接进入等待,直到超时。这个超时时间可以通过参数innodb_lock_wait_timeout来设置。默认50s,对于在线服务来说,这个等待时间往往是无法接受的。但是如果设为较短的时间,对于稍长一点的锁等待会产生误伤。
(2)发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数innodb_deadlock_detect设置为on,表示开启这个逻辑。但是死锁检测也是需要额外负担的。开启后并不是所有事务都要进行死锁检测,只有要加锁访问的行上有锁才检测。
MVCC
注意:虽然低水位是视图数组里的最小值,但是高水位并不是视图数组里的最大值。