学习

maven

转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!

这部分内容主要根据 Maven 官方文档整理,做了对应的删减,主要保留比较重要的部分,不涉及实战,主要是一些重要概念的介绍。

Maven 介绍 #

Maven 官方文档是这样介绍的 Maven 的:

Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project’s build, reporting and documentation from a central piece of information.

Apache Maven 的本质是一个软件项目管理理解工具。基于项目对象模型 (Project Object Model,POM) 的概念,Maven 可以从一条中心信息 管理项目的构建报告文档

什么是 POM? 每一个 Maven 工程都有一个 pom.xml 文件,位于根目录中,包含项目构建生命周期的详细信息。通过 pom.xml 文件,我们可以定义项目坐标项目依赖项目信息插件信息等等配置。

对于开发者来说,Maven 的主要作用主要有 3 个:

  1. 项目构建 :提供标准的、跨平台自动化项目构建方式。
  2. 依赖管理方便快捷管理项目依赖的资源jar 包),避免资源间的版本冲突问题。
  3. 统一开发结构 :提供标准的、统一项目结构

关于 Maven 的基本使用这里就不介绍了,建议看看官网的 5 分钟上手 Maven 的教程: Maven in 5 Minutes

...

Atomic预备知识

Java实现CAS的原理[非javaguide] #

i是非线程安全的,因为**i不是原子操作;可以使用synchronized和CAS实现加锁**

synchronized是悲观锁,一旦获得锁,其他线程进入后就会阻塞等待锁;而CAS是乐观锁,执行时不会加锁,假设没有冲突,如果因为冲突失败了就重试,直到成功

  • 乐观锁和悲观锁

    • 这是一种分类方式
    • 悲观锁,总是认为每次访问共享资源会发生冲突,所以必须对每次数据操作加锁,以保证临界区的程序同一时间只能有一个线程在执行
    • 乐观锁,又称**“无锁”**,假设对共享资源访问没有冲突,线程可以不停的执行,无需加锁无需等待;一旦发生冲突,通常是使用一种称为CAS的技术保证线程执行安全
      • 无锁没有锁的存在,因此不可能发生死锁,即乐观锁天生免疫死锁
      • 乐观锁用于**“读多写少”的环境,避免加锁频繁影响性能;悲观锁用于“写多读少”,避免频繁失败及重试**影响性能
  • CAS概念,即CompareAndSwap ,比较和交换,CAS中,有三个值(概念上)
    V:要更新的变量(var);E:期望值(expected);N:新值(new) 判断V是否等于E,如果等于,将V的值设置为N;如果不等,说明已经有其它线程更新了V,则当前线程放弃更新,什么都不做。 一般来说,预期值E本质上指的是“旧值”(判断是否修改了)

    1. 如果有一个多个线程共享的变量i原本等于5,我现在在线程A中,想把它设置为新的值6;
    2. 我们使用CAS来做这个事情;
    3. (首先要把原来的值5在线程中保存起来)
    4. 接下来是原子操作:首先我们用(现在的i)去与5对比,发现它等于5,说明没有被其它线程改过,那我就把它设置为新的值6,此次CAS成功,i的值被设置成了6;
    5. 如果不等于5,说明i被其它线程改过了(比如现在i的值为2),那么我就什么也不做,此次CAS失败,i的值仍然为2。

其中i为V,5为E,6为N

CAS是一种原子操作,它是一种系统原语,是一条CPU原子指令,从CPU层面保证它的原子性(不可能出现说,判断了对比了i为5之后,正准备更新它的值,此时该值被其他线程改了

多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新其余均会失败,但失败的线程并不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。

  • Java实现CAS的原理 - Unsafe类

    • 在Java中,如果一个方法是native的,那Java就不负责具体实现它,而是交给底层的JVM使用c或者c++去实现

    • Java中有一个Unsafe类,在sun.misc包中,里面有一些native方法,其中包括:

      boolean compareAndSwapObject(Object o, long offset,Object expected, Object x);
      boolean compareAndSwapInt(Object o, long offset,int expected,int x);
      boolean compareAndSwapLong(Object o, long offset,long expected,long x);
      
      
      //------>AtomicInteger.class
      
      public class AtomicInteger extends Number implements java.io.Serializable {
      private static final long serialVersionUID = 6214790243416807050L;
      
      // setup to use Unsafe.compareAndSwapInt for updates
      private static final Unsafe unsafe = Unsafe.getUnsafe();
      private static final long valueOffset;
      
      static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
      }
      
      private volatile int value;
      public final int getAndIncrement() {
      	return unsafe.getAndAddInt(this, valueOffset, 1);
      
    }
    

    }

    ...

MySQL高性能优化规范建议总结

转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!

索引优化相关

  1. in 代替 or
  2. not exist 代替 not in

数据库命名规范 #

  • 所有数据库对象名称必须使用小写字母并用下划线分割
  • 所有数据库对象名称禁止使用 MySQL 保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来)
  • 数据库对象的命名要能做到见名识意,并且最好不要超过 32 个字符
  • 临时库表必须tmp_ 为前缀并以日期为后缀,备份表必须bak_ 为前缀以日期 (时间戳) 为后缀
  • 所有存储相同数据的列名和列类型必须一致(一般作为关联列,如果查询时关联列类型不一致会自动进行数据类型隐式转换,会造成列上的索引失效,导致查询效率降低)

数据库基本设计规范 #

所有表必须使用InnoDB存储引擎 #

  • 没有特殊要求(即 InnoDB 无法满足的功能如:列存储,存储空间数据等)的情况下,所有表必须使用 InnoDB 存储引擎(MySQL5.5 之前默认使用 Myisam,5.6 以后默认的为 InnoDB)。
  • InnoDB 支持事务,支持行级锁,更好的恢复性高并发下性能更好

数据库和表的字符集统一使用UTF-8 #

兼容性更好,统一字符集可以避免由于字符集转换产生的乱码,不同的字符集进行比较前需要进行转换会造成索引失效,如果数据库中有存储 emoji 表情的需要,字符集需要采用 utf8mb4 字符集。

参考文章:

所有表和字段都需要添加注释 #

使用 comment 从句添加表列的备注,从一开始就进行数据字典的维护

尽量控制单表数据量的大小,建议控制在500万以内 #

  • 500 万并不是 MySQL 数据库的限制,过大会造成修改表结构备份恢复都会有很大的问题。

    ...

MySQL常见面试题总结

转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!====

MySQL基础 #

关系型数据库介绍 #

  • 关系型数据库,建立在关系模型的基础上的数据库。表明数据库中所存储的数据之间的联系(一对一、一对多、多对多)
  • 关系型数据库中,我们的数据都被存放在各种表中(比如用户表),表中的每一行存放着一条数据(比如一个用户的信息) 关系型数据库表关系
  • 大部分关系型数据库都使用SQL来操作数据库中的数据,并且大部分关系型数据库都支持事务四大特性(ACID)

常见的关系型数据库
MySQLPostgreSQLOracleSQL ServerSQLite微信本地的聊天记录的存储就是用的 SQLite) ……

MySQL介绍 #

img

  • MySQL是一种关系型数据库,主要用于持久化存储我们系统中的一些数据比如用户信息

  • 由于 MySQL 是开源免费并且比较成熟的数据库,因此,MySQL 被大量使用在各种系统中。任何人都可以在 GPL(General Public License 通用性公开许可证) 的许可下下载并根据个性化的需要对其进行修改。MySQL 的默认端口号是3306

MySQL基础架构 #

  • MySQL的一个简要机构图,客户端的一条SQL语句在MySQL内部如何执行 ly-20241212141911246
  • MySQL主要由几部分构成
    1. 连接器身份认证权限相关(登录MySQL的时候)
    2. 查询缓存:执行查询语句的时候,会先查询缓存(MySQL8.0版本后移除,因为这个功能不太实用)
    3. 分析器没有命中缓存的话,SQL语句就会经过分析器,分析器说白了就是要先看你的SQL语句要干嘛,再检查你的SQL语句语法是否正确
    4. 优化器:按照MySQL认为最优的方案去执行
    5. 执行器执行语句,然后从存储引擎返回数据。执行语句之前会先判断是否有权限,如果没有权限,就会报错
    6. 插件式存储引擎:主要负责数据存储读取,采用的是插件式架构,支持InnoDBMyISAMMemory等多种存储引擎

MySQL存储引擎 #

MySQL核心在于存储引擎

MySQL支持哪些存储引擎?默认使用哪个? #

  • MySQL支持多种存储引擎,可以通过show engines命令来查看MySQL支持的所有存储引擎 查看 MySQL 提供的所有存储引擎

  • 默认存储引擎为InnoDB,并且,所有存储引擎中只有InnoDB是事务性存储引擎,也就是说只有InnoDB支持事务

  • 这里使用MySQL 8.x MySQL 5.5.5之前,MyISAM是MySQL的默认存储引擎;5.5.5之后,InnoDB是MySQL的默认存储引擎,可以通过select version()命令查看你的MySQL版本

    mysql> select version();
    +-----------+
    | version() |
    +-----------+
    | 8.0.27    |
    +-----------+
    1 row in set (0.00 sec) 
    

    使用show variables like %storage_engine%命令直接查看MySQL当前默认的存储引擎
    查看 MySQL 当前默认的存储引擎

    ...

MySQL中的隐式转换造成的索引失效

转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!

本篇文章基于MySQL 5.7.26,原文:https://www.guitu18.com/post/2019/11/24/61.html

前言 #

  • 关于数据库优化,最常见的莫过于索引失效,数据量多的时候比较明显,处理不及时会造成雪球效应,最终导致数据库卡死甚至瘫痪
  • 这里说的是隐式转换造成的索引失效

数据准备 #

-- 创建测试数据表
DROP TABLE IF EXISTS test1;
CREATE TABLE `test1` (
    `id` int(11) NOT NULL,
    `num1` int(11) NOT NULL DEFAULT '0',
    `num2` varchar(11) NOT NULL DEFAULT '',
    `type1` int(4) NOT NULL DEFAULT '0',
    `type2` int(4) NOT NULL DEFAULT '0',
    `str1` varchar(100) NOT NULL DEFAULT '',
    `str2` varchar(100) DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `num1` (`num1`),
    KEY `num2` (`num2`),
    KEY `type1` (`type1`),
    KEY `str1` (`str1`),
    KEY `str2` (`str2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 创建存储过程
DROP PROCEDURE IF EXISTS pre_test1;
DELIMITER //
CREATE PROCEDURE `pre_test1`()
BEGIN
    DECLARE i INT DEFAULT 0;
    SET autocommit = 0;
    WHILE i < 10000000 DO
        SET i = i + 1;
        SET @str1 = SUBSTRING(MD5(RAND()),1,20);
        -- 每100条数据str2产生一个null值
        IF i % 100 = 0 THEN
            SET @str2 = NULL;
        ELSE
            SET @str2 = @str1;
        END IF;
        INSERT INTO test1 (`id`, `num1`, `num2`,
        `type1`, `type2`, `str1`, `str2`)
        VALUES (CONCAT('', i), CONCAT('', i),
        CONCAT('', i), i%5, i%5, @str1, @str2);
        -- 事务优化,每一万条数据提交一次事务
        IF i % 10000 = 0 THEN
            COMMIT;
        END IF;
    END WHILE;
END;
// DELIMITER ;
-- 执行存储过程
CALL pre_test1(); 

其中,七个字段,首先使用存储过程生成 1000 万条测试数据, 测试表一共建立了 7 个字段(包括主键),num1num2保存的是和ID一样的顺序数字,其中num2是字符串类型type1type2保存的都是主键对 5 的取模,目的是模拟实际应用中常用类似 type 类型的数据,但是**type2是没有建立索引的。 str1str2都是保存了一个 20 位长度的随机字符串str1不能为NULLstr2允许为NULL,相应的生成测试数据的时候我也会str2字段生产少量NULL值**(每 100 条数据产生一个NULL值)。

...

MySQL数据库时间类型数据存储建议

转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!

不要用字符串存储日期 #

  • 优点:简单直白
  • 缺点
    1. 字符串占有的空间更大
    2. 字符串存储的日期效率比较低(逐个字符进行比较),无法用日期相关的API进行计算和比较

Datetime和Timestamp之间抉择 #

Datetime 和 Timestamp 是 MySQL 提供的两种比较相似的保存时间的数据类型。他们两者究竟该如何选择呢?

通常我们都会首选 Timestamp

Datetime类型没有时区信息 #

  1. DateTime 类型是没有时区信息的(时区无关) ,DateTime 类型保存的时间都是当前会话所设置的时区对应的时间。这样就会有什么问题呢?当你的时区更换之后,比如你的服务器更换地址或者更换客户端连接时区设置的话,就会导致你从数据库中读出的时间错误。不要小看这个问题,很多系统就是因为这个问题闹出了很多笑话。
  2. Timestamp 和时区有关。Timestamp 类型字段的值会随着服务器时区的变化而变化,自动换算成相应的时间,说简单点就是在不同时区查询到同一个条记录此字段的值会不一样

案例

-- 建表
CREATE TABLE `time_zone_test` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `date_time` datetime DEFAULT NULL,
  `time_stamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

-- 插入数据
INSERT INTO time_zone_test(date_time,time_stamp) VALUES(NOW(),NOW());
-- 查看数据
select date_time,time_stamp from time_zone_test;
-- 结果
/*
 +---------------------+---------------------+
| date_time           | time_stamp          |
+---------------------+---------------------+
| 2020-01-11 09:53:32 | 2020-01-11 09:53:32 |
+---------------------+---------------------+
------ 
*/

修改时区并查看数据

...

SQL语句在MySQL中的执行过程

转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!

原文 https://github.com/kinglaw1204 感谢作者

  • 本篇文章会分析一个SQL语句在MySQL的执行流程,包括SQL的查询在MySQL内部会怎么流转SQL语句的更新是怎么完成的
  • 分析之前先看看MySQL的基础架构,知道了MySQL由哪些组件组成以及这些组件的作用是什么,可以帮助我们理解解决这些问题

MySQL基础架构分析 #

ly-20241212141908611

MySQL基本架构概览 #

  • 下图是MySQL的简要架构图,从下图可以看到用户的SQL语句在MySQL内部是如何执行的
  • 先简单介绍一个下图涉及的一些组件的基本作用 ly-20241212141908907
    1. 连接器身份认证权限相关(登录MySQL的时候)
    2. 查询缓存:执行查询语句的时候,会先查询缓存(MySQL8.0版本后移除,因为这个功能不太实用)
    3. 分析器没有命中缓存的话,SQL语句就会经过分析器,分析器说白了就是要先看你的SQL语句干嘛,再检查你的SQL语句语法是否正确
    4. 优化器:按照MySQL认为最优的方案去执行
    5. 执行器执行语句,然后从存储引擎返回数据
  • 简单来说 MySQL 主要分为 Server 层和存储引擎层:
    • Server 层:主要包括连接器查询缓存分析器优化器执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程触发器视图函数等,还有一个通用的日志模块 binlog 日志模块
    • 存储引擎: 主要负责数据的存储读取,采用可以替换的插件式架构,支持 InnoDB、MyISAM、Memory 等多个存储引擎,其中 InnoDB 引擎有自有的日志模块 redolog 模块现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5 版本开始就被当做默认存储引擎了

Server层基本组件介绍 #

ly-20241212141909073

  1. 连接器 连接器主要和身份认证权限相关的功能相关,就好比一个级别很高的门卫一样

    主要负责用户登录数据库,进行用户的身份认证,包括校验账户密码,权限等操作,如果用户账户密码已通过,连接器会到权限表中查询该用户的所有权限,之后在这个连接里的权限逻辑判断都是会依赖此时读取到的权限数据,也就是说,后续只要这个连接不断开即使管理员修改了该用户的权限,该用户也是不受影响的。

  2. 查询缓存(MySQL8.0 版本后移除)
    查询缓存主要用来缓存我们所执行的 SELECT 语句以及该语句的结果集

    • 连接建立后,执行查询语句的时候,会先查询缓存,MySQL 会先校验这个 SQL 是否执行过,以 Key-Value 的形式缓存在内存中,Key 是查询预计,Value 是结果集。如果缓存 key 被命中,就会直接返回给客户端,如果没有命中,就会执行后续的操作,完成后也会把结果缓存起来,方便下一次调用。当然在真正执行缓存查询的时候还是会校验用户的权限,是否有该表的查询条件。

    • MySQL 查询不建议使用缓存,因为查询缓存失效在实际业务场景中可能会非常频繁,假如你对一个表更新的话,这个表上的所有的查询缓存都会被清空。对于不经常更新的数据来说,使用缓存还是可以的。所以,一般在大多数情况下我们都是不推荐去使用查询缓存的。

    MySQL 8.0 版本后删除了缓存的功能,官方也是认为该功能在实际的应用场景比较少,所以干脆直接删掉了

    ...

innodb引擎对MVCC的实现

转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!

一致性非锁定读和锁定读 #

一致性非锁定读 #

★★非锁定★★

  • 对于一致性非锁定读(Consistent Nonlocking Reads)的实现,通常做法是加一个版本号或者时间戳字段,在更新数据的同时版本号+1或者更新时间戳。查询时,将当前可见的版本号对应记录的版本号进行比对,如果记录的版本小于可见版本,则表示该记录可见
  • InnoDB存储引擎中,多版本控制(multi versioning)即是非锁定读的实现。如果读取的行正在执行DELETEUPDATE操作,这时读取操作不会去等待行上 锁的释放.相反地,Inn哦DB存储引擎会去读取行的一个快照数据,对于这种读取历史数据的方式,我们叫它快照读(snapshot read)
  • Repeatable ReadRead Committed 两个隔离级别下,如果是执行普通的 select 语句(不包括 select ... lock in share mode ,select ... for update)则会使用 一致性非锁定读(MVCC)。并且在 Repeatable ReadMVCC 实现了可重复读和防止部分幻读

锁定读 #

  • 如果执行的是下列语句,就是锁定读(Locking Reads)

    1. select ... lock in share

    2. select ... for update

    3. insert upatedelete

    4. 锁定读下,读取的是数据的最新版本,这种读也被称为当前读current read锁定读会对读取到的记录加锁

    5. select ... lock in share mode :对(读取到的)记录加S锁,其他事务也可以加S锁,如果加X锁则会被阻塞

    6. select ... for updateinsertupdatedelete:对记录加X锁,且其他事务不能加任何锁

  • 在一致性非锁定读下,即使读取的记录已被其他事务加上X锁,这时记录也是可以被读取的,即读取的快照数据

    ...

MySQL事务隔离级别详解

转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!

事务隔离级别总结 #

  • SQL标准定义了四个隔离级别

    1. READ-UNCOMMITTED(读取未提交)最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
    2. READ-COMMITED(读取已提交):允许读取并发事务 已经提交的数据,可以阻止脏读,但是幻读不可重复读仍有可能发生
    3. REPEATABLE-READ(可重复读):对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读不可重复读,但幻读仍有可能发生
    4. SERIALIZABLE(可串行化)最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读不可重复读以及幻读
    隔离级别脏读不可重复读幻读
    READ-UNCOMMITTED
    READ-COMMITTED×
    REPEATABLE-READ××
    SERIALIZABLE×××
  • MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)

    使用命令查看,通过SELECT @@tx_isolation;
    MySQL 8.0 该命令改为SELECT @@transaction_isolation;

    MySQL> SELECT @@tx_isolation;
    +-----------------+
    | @@tx_isolation  |
    +-----------------+
    | REPEATABLE-READ |
    +-----------------+ 
    
  • 从上面对SQL标准定义了四个隔离级别的介绍可以看出,标准的SQL隔离级别里,REPEATABLE-READ(可重复读)是不可以防止幻读的。但是,InnoDB实现的REPEATABLE-READ 隔离级别其实是可以解决幻读问题发生的,分两种情况

    1. 快照读:由MVCC机制来保证不出现幻读
    2. 当前读:使用Next-Key Lock进行加锁来保证不出现幻读,Next-Key Lock是行锁(Record Lock )和间隙锁(Gap Lock)的结合,行锁只能锁住已经存在的行,为了避免插入新行,需要依赖间隙锁 (只用间隙锁不行,因为间隙锁是 > 或 < ,不包括等于,所以再可重复读下原记录可能会被删掉)

    因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 READ-COMMITTED ,但是你要知道的是 InnoDB 存储引擎默认使用 REPEATABLE-READ 并不会有任何性能损失。

    ...

日志

转载自https://github.com/Snailclimb/JavaGuide(添加小部分笔记)感谢作者!

前言 #

  • 首先要了解一个东西 :WAL,全称 Write-Ahead Logging,它的关键点就是先写日志,再写磁盘

    在概念上,innodb通过***force log at commit***机制实现事务的持久性,即在事务提交的时候,必须先将该事务的所有事务日志写入到磁盘上的redo log file和undo log file中进行持久化

    1. WAL 机制的原理也很简单:修改并不直接写入到数据库文件中,而是写入到另外一个称为 WAL 的文件中;如果事务失败,WAL 中的记录会被忽略,撤销修改;如果事务成功,它将在随后的某个时间被写回到数据库文件中,提交修改

    2. 使用 WAL 的数据库系统不会再每新增一条 WAL 日志就将其刷入数据库文件中,一般积累一定的量然后批量写入,通常使用页为单位,这是磁盘的写入单位。 同步 WAL 文件和数据库文件的行为被称为 checkpoint(检查点),一般在 WAL 文件积累到一定页数修改的时候;当然,有些系统也可以手动执行 checkpoint。执行 checkpoint 之后,WAL 文件可以被清空,这样可以保证 WAL 文件不会因为太大而性能下降。

      有些数据库系统读取请求也可以使用 WAL,通过读取 WAL 最新日志就可以获取到数据的最新状态

      关于checkpoint:https://www.cnblogs.com/chenpingzhao/p/5107480.html思考一下这个场景:如果重做日志可以无限地增大,同时缓冲池也足够大 ,那么是不需要将缓冲池中页的新版本刷新回磁盘。因为当发生宕机时,完全可以通过重做日志来恢复整个数据库系统中的数据到宕机发生的时刻。但是这需要两个前提条件:1、缓冲池可以缓存数据库中所有的数据;2、重做日志可以无限增大

      因此Checkpoint(检查点)技术就诞生了,目的是解决以下几个问题:1、缩短数据库的恢复时间;2、缓冲池不够用时,将脏页刷新到磁盘;3、重做日志不可用时,刷新脏页

      • 当数据库发生宕机时,数据库不需要重做所有的日志,因为Checkpoint之前的页都已经刷新回磁盘。数据库只需对Checkpoint后的重做日志进行恢复,这样就大大缩短了恢复的时间。
      • 当缓冲池不够用时,根据LRU算法会溢出最近最少使用的页,若此页为脏页,那么需要强制执行Checkpoint,将脏页也就是页的新版本刷回磁盘。
      • 当重做日志出现不可用时,因为当前事务数据库系统对重做日志的设计都是循环使用的,并不是让其无限增大的,重做日志可以被重用的部分是指这些重做日志已经不再需要,当数据库发生宕机时,数据库恢复操作不需要这部分的重做日志,因此这部分就可以被覆盖重用。如果重做日志还需要使用,那么必须强制Checkpoint,将缓冲池中的页至少刷新到当前重做日志的位置。
    3. mysql 的 WAL,大家可能都比较熟悉。mysql 通过 redo、undo 日志实现 WAL。redo log 称为重做日志,每当有操作时,在数据变更之前将操作写入 redo log,这样当发生掉电之类的情况时系统可以在重启后继续操作。undo log 称为撤销日志,当一些变更执行到一半无法完成时,可以根据撤销日志恢复到变更之间的状态。mysql 中用 redo log 来在系统 Crash 重启之类的情况时修复数据(事务的持久性),而 undo log 来保证事务的原子性。

      ...