Postgresql杂谈 20—详解Postgresql中的Checkpoint、WAL日志和热备份恢复

2023-10-27

       本文中,我们共同学习下Postgresql的WAL日志。WAL,是Write Ahead Log的简称,翻译过来就是预写日志,或者叫做重做日志。相信大家对数据库事务的四大特性ACID(原子性、一致性、隔离性和持久性)已经非常熟悉了,今天我们先从数据库数据的持久性说起。数据的持久性是指,数据库中的事务中的数据一旦提交,便可以做到持久化,只要你不删除,那么数据将会永远存在数据库中。

一、有关CheckPoint

       现在我们就来讨论下这个提交的过程。我们直到数据库中数据的存放位置最终是在磁盘上,但是磁盘IO是一个很耗时的操作,当我们查询数据时,如果每次查询都从磁盘直接查询,那么大量的IO,尤其是随机IO,会吃掉大量的时间。所以数据库中提出了共享内存的概念,将一部分数据先读到内存中,查询时直接从内存查询,这就大大提高了查询效率。另一方面,如果修改,也只是在内存中进行修改,那么,这就随之而带来了新的问题:如何保证在内存中的数据和磁盘一致?显然需要一定的时机将内存中的数据重新持久化到磁盘。内存中和磁盘中不一致的数据块,我们称作脏块,而脏块同步到磁盘的过程,就是一个Checkpoint的过程。

       Postgresql的checkpoint的上可以分为两种,自动checkpoint和手动执行checkpoint命令,两个过程的结果都是将脏块写入到了磁盘。自动的checnpoint是按照一定的时间间隔执行checkpoint命令,时间间隔是在postgresql.conf文件中可配的,默认是5分钟。可以通过show命令查看配置的这个参数:

stock_analysis_data=# show checkpoint_timeout;
 checkpoint_timeout 
--------------------
 5min
(1 row)

       但是,这个参数是无法用set命令修改的,如果修改,会提示下面的错误:‘

stock_analysis_data=# set checkpoint_timeout='10min';
ERROR:  parameter "checkpoint_timeout" cannot be changed now

       因为它是属于数据库全局的配置,不属于某个会话,只能通过修改postgresql.conf文件,然后重启服务。

       除了上述自动checkpoint之外,我们也可以手动执行checkpoint命令,这样也最终使内存中的脏块刷新到了磁盘上,完成了数据的持久化。

stock_analysis_data=# checkpoint;
CHECKPOINT

       在checkpoint之后,通过查看控制文件可以看到哪个 WAL文件的内容已经被持久化:

[root@VM-115-39-centos bin]# ./pg_controldata  -D /var/lib/pgsql/11/data/
pg_control version number:            1100
Catalog version number:               201809051
Database system identifier:           6853342000172018996
Database cluster state:               in production
pg_control last modified:             Fri 09 Jul 2021 09:56:48 AM CST
Latest checkpoint location:           2/E8C05538
Latest checkpoint's REDO location:    2/E8C05500
Latest checkpoint's REDO WAL file:    0000000100000002000000E8
Latest checkpoint's TimeLineID:       1
Latest checkpoint's PrevTimeLineID:   1
Latest checkpoint's full_page_writes: on
Latest checkpoint's NextXID:          0:353416
Latest checkpoint's NextOID:          36156
Latest checkpoint's NextMultiXactId:  1
Latest checkpoint's NextMultiOffset:  0
Latest checkpoint's oldestXID:        561
Latest checkpoint's oldestXID's DB:   1
Latest checkpoint's oldestActiveXID:  353416
Latest checkpoint's oldestMultiXid:   1
Latest checkpoint's oldestMulti's DB: 1
Latest checkpoint's oldestCommitTsXid:0
Latest checkpoint's newestCommitTsXid:0
Time of latest checkpoint:            Fri 09 Jul 2021 09:56:48 AM CST
Fake LSN counter for unlogged rels:   0/1
Minimum recovery ending location:     0/0
Min recovery ending loc's timeline:   0
Backup start location:                0/0
Backup end location:                  0/0
End-of-backup record required:        no
wal_level setting:                    replica
wal_log_hints setting:                off
max_connections setting:              100
max_worker_processes setting:         8
max_prepared_xacts setting:           10
max_locks_per_xact setting:           64
track_commit_timestamp setting:       off
Maximum data alignment:               8
Database block size:                  8192
Blocks per segment of large relation: 131072
WAL block size:                       8192
Bytes per WAL segment:                16777216
Maximum length of identifiers:        64
Maximum columns in an index:          32
Maximum size of a TOAST chunk:        1996
Size of a large-object chunk:         2048
Date/time type storage:               64-bit integers
Float4 argument passing:              by value
Float8 argument passing:              by value
Data page checksum version:           0
Mock authentication nonce:            e6868f1dca4d434a9fb50334b182677fd3a9881e0ca2cd9560c3baa41c3c3622
[root@VM-115-39-centos bin]# 

       我们看第8行Latest checkpoint's REDO location这个参数,它表示在进行checkpoint时,当前持久化的点记录在了哪个WAL文件,也就是说在这个点之前的所有数据都已经刷到磁盘,我们可以执行下面的命令查看这个文件,参数就是Latest checkpoint's REDO location的值:

stock_analysis_data=# select pg_walfile_name('2/E8C05538');
     pg_walfile_name      
--------------------------
 0000000100000002000000E8
(1 row)

二、WAL日志的作用

       我们在上文中说过,Postgresql默认每5分钟自动进行一次checkpoint,把内存中的脏块同步到磁盘上,这就又会引起另外一个问题:假设数据库在早上8:00钟自动进行了一次checkpoint,那么下一次执行checkpoint的时间会是在8:05,如果8:00~8:05这段时间内又有新的数据写入,而且写入后数据库发生了宕机重启,那这部分新的数据岂不是就丢失了?于是,为了解决这个问题,WAL日志就登场了。Postgresql的WAL日志、Oracle中的Redo日志、以及Mysql的binlog都是这个作用。

2.1 WAL日志的目录

       Postgresql中redo日志在数据库的数据目录的pg_wal(Postgresql9.X之前叫pg_xlog)目录下,查看所有日志文件如下:

[root@VM-115-39-centos pg_wal]# pwd
/var/lib/pgsql/11/data/pg_wal
[root@VM-115-39-centos pg_wal]# ls 
000000010000000000000057  0000000100000000000000DB  00000001000000010000005F  0000000100000001000000E3  000000010000000200000067
000000010000000000000058  0000000100000000000000DC  000000010000000100000060  0000000100000001000000E4  000000010000000200000068
......

       我们看到,所谓的WAL日志其实就是这些由24位16进制数字命名的文件。那么WAL日志是怎么解决在自动checkpoint间隔内数据库宕机而数据不会丢失呢?实际上原理很简单,在更新内存中的数据同时,Postgresql会将数据同步保存到WAL日志上,一旦数据库发生了宕机再重启,它会根据前次checkpoint的时间点作为起始点,根据WAL日志更新起始点之后的数据,这就是所谓的’前滚‘过程。

       可能有的朋友会有疑问:同样是写磁盘,为什么Postgresql在用户提交数据时,没有将数据直接写入到数据表磁盘上,而是写入到了WAL日志中?这就涉及到了重做日志写入算法效率的问题。

2.2 重做日志算法

       我们直到磁盘IO一直时操作数据效率的一大瓶颈,而写WAL日志时,数据库采用了一种叫做重做日志算法的一种磁盘写入算法,在相同的数据量下,采用这种算法写入磁盘文件比普通的文件写入效率要提高一倍以上,所以Postresql或者其它数据库都是采用的这种,先更新内存数据和重写日志,最终将脏块同步到磁盘的方式,保证数据的安全性和持久性。

2.3 WAL文件的文件名解析

       在上文中已经谈到过,WAL文件的文件名是由24位16进制的数字组成的,那么可以执行下面的命令来查看数据库当前正在使用的WAL文件,并以此文件名为例来说明下WAL文件名的含义:

stock_analysis_data=# select pg_walfile_name(pg_current_wal_lsn());
     pg_walfile_name      
--------------------------
 0000000100000002000000E8

       整个文件名0000000100000002000000E8,其实分成了三个部分。但是,在解释每个部分的含义之前,我们还必须引入另外一个概念——LSN。LSN,全称是Log Sequence Number 翻译过来就是日志序列号,是一个Postgresql全局的不断增长的8子节长度的数字,它会随着WAL日志的不断增加而不断地增长。

        现在,我们回过头来,再看下WAL日志三个组成部分:

  • 第一部分,叫做时间线,是从1开始递增地数字,很显然,当第二、三部分数字达到最大值之后,第一位会递增1。
  • 第二部分,叫做LogId,实际上是LSN的高32位。
  • 第三部分,叫做LogSeg,是LSN的低32位除以WAL文件的大小。WAL文件的大小默认是16M,但是可以在initdb时指定修改。

 

2.4 WAL文件的循环复用原理

       如果单单从pg_wal目录下各个文件名称来看,WAL日志文件一直是在滚动更新的,旧文件不断地在删除、新文件不断地增加。我们又了解到,磁盘IO实际上效率是非常低的,那如果在WAL更新的过程中一方面要创建新文件,另一方面要删除旧文件,那岂不是会很耗时?其实,这是Postgresql玩的一手障眼法,当checkout命令执行后,旧的WAL文件也就没用了,当需要产生新的WAL文件时,并不是真正生成了新的文件,而是将最老的一个WAL文件进行了重命名。而且重命名也并不是我们传统意义上的rename,而是为旧文件创建了一个硬链接,然后再删除旧文件。

三、使用WAL日志进行热备份

       WAL日志还有另外一个作用就是可以 利用其进行热备份(也就是不停服务,在线备份),使用WAL日志进行备份具有两种方法,一种是独占型的,另外一种是非独占型的,两者的主要区别在于:

1)前者不允许多个备份命令同时进行,而后者允许多个命令同时执行;

2)前者会在源数据目录下自动生成backup_label文件,只要有这个文件存在,数据库重启时就会从backup_label指定的checkpoint点开始恢复数据,而不是从最新的一次checkpoint开始,而后者需要我们手动创建backup_lable文件。

3)正是基于第2点原因,当我们在使用独占型的备份方式时,一旦未来得及删除backup_label文件,数据库发生了宕机,那么数据库重启后,就会从backup_label记录的checkpoint点开始恢复,就会加大前滚耗时。而且如果数据量比较大,造成从某些未来及持久化的WAL文件丢失,数据库就会报错,无法启动。

       基于上述特点,在生产环境中备份建议采用非独占型的,但是在本次演示中,由于相对比较简单,笔者采用独占式的方式进行备份。

       下面,笔者来演示下这个过程:

第1步,通过pg_start_backup()方法开始热备份。

 stock_analysis_data=# select pg_start_backup('mydb202107091430');
 pg_start_backup 
-----------------
 2/E9000028

       可以看到在数据库的源目录下多了一个backup_lable的文件:

       文件的内容如下:

START WAL LOCATION: 2/E9000028 (file 0000000100000002000000E9)
CHECKPOINT LOCATION: 2/E9000060
BACKUP METHOD: pg_start_backup
BACKUP FROM: master
START TIME: 2021-07-09 14:30:55 CST
LABEL: mydb202107091430
START TIMELINE: 1

       主要是记录了备份开始时的点CHECKPOINT LOCATION,其实pg_start_backup命令本身就有两个作用:

1)执行checkpoint命令。

2)置写日志标志为:XLogCtl->Insert.forcePageWrites = true,也就是把这个标志设置为true后,数据库会把变化的整个数据块都记录到WAL文件中,而不仅仅是块中行的变化。

       第一个作用很容易理解,这里不再多做解释。第二个作用主要是为了我们在做热备份的下一步考虑。当我们拷贝整个数据库源目录时,因为拷贝的是正在使用的数据块,很可能拷贝的块前半部分是旧数据,后半部分是新数据,如果WAL日志不是按照块记录变化,而是按行,这种不一致的块在将来数据重做时很难恢复。

第2步,通过cpio命令进行数据库源目录的备份

find /var/lib/pgsql/11/data | cpio -ocvB > /data/mydata.cpio

第3步,调用pg_stop_backup()结束备份

select pg_stop_backup();

       pg_stop_backup主要有两个作用:

1)删除backup_label文件。

2)在WAL记录中记录一个XLOG_BACKUP_END的标记,而且会强制Postgresql开始使用下一个WAL文件。

       比如在我本机上,在执行第3步之前,查看当前用的WAL文件:

postgres=# select pg_walfile_name(pg_current_wal_lsn());
     pg_walfile_name      
--------------------------
 0000000100000002000000FD
(1 row)

       在执行第三步之后:

postgres=# select pg_walfile_name(pg_current_wal_lsn());
     pg_walfile_name      
--------------------------
 0000000100000002000000FE

       发现WAL文件名称加了1。

第4步,这一步很重要,就是要复制源目录下所有的WAL文件作为备份。

       原因是我们需要拷贝被标记了XLOG_BACKUP_END的WAL文件,在恢复时才能知道恢复截止到哪个点,在这个点之后的WAL文件数据全部都不要了。这个点也就是我们执行pg_stop_backup()时那个点。

       结果了上述步骤,我们就完成了数据库的热备份,其实恢复起来就比较简单了:

(1)使用cpio命令将备份的文件恢复到目标目录:

cpio –icduv < /data/mydata.cpio

(2)然后将第4步备份的WAL文件复制到目标目录的pg_wal目录下。

       至此,有关Postgresql进行热备份和恢复的内容就介绍完了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Postgresql杂谈 20—详解Postgresql中的Checkpoint、WAL日志和热备份恢复 的相关文章

随机推荐

  • MFC主要知识点

    WIN32 1 WM COMMAND是由菜单 加速键 工具栏按钮 按钮发出 wParam包含ID lParam包含句柄 2 LOWORD 就是取低字节的 将DWORD转成WORD 3 消息处理函数返回TRUE 表示你处理过的 return
  • 贪心算法--字典序最小的字符串

    贪心 给定一个字符串组成的数组strs 把所有的字符串拼接起来 返回所有的拼接结果中 字典序最小的结果 字典序 字符串长度相同时 当做26位的数 直接比较大小abc lt acd 字符串长度不相同时 在后面补上0 变成相同的再比较 abc
  • 用Excel求线性回归方程

    文章目录 一 何为线性回归 二 如何制作线性回归 最小二乘法 三 利用Excel求线性回归方程 操作方法 1 选择需要拟合的数据 2 点击工具栏的插入 选择插入散点图 3 在生成的表格右上角勾选上生成渐近线 4 右击生成的渐近线 选择设置渐
  • 通俗大白话,彻底弄懂 https 原理本质

    通俗大白话 彻底弄懂 https 原理本质 相信很多人 对 https 的过程弄不清楚 只是知道 https是安全加密的 背后的原理 过程并不清楚 笔者曾经也是对https的过程并不清楚 一知半解 而且最可气的是每次面试 面试官很可能就问你
  • 未来发展?智能AI革命与脑联网领域

    前言 最近在看一些关于未来技术的一些书 分享一下我的感受吧 历史革命 智能制造是国家战略的共同核心 各国都期望借助于自动化 数字化 网络化 智能化手段 减少对人的依赖 实现各自国家向高质 高效 高端 绿色 高竞争力方向发展 机械革命 第一次
  • Java最新大厂面试真题总结:怎么使用rke安装k8s集群

    阿里P8级架构师第九篇 千亿流量高并发高可用分布式系统之数据治理篇 阿里P8级架构师第十篇 千亿流量高并发高可用分布式系统之人工智能加成篇 数据融合模块 构建画像模块 召回策略模块 排序模型模块ctr预估 微服务模块 AB Test模块 S
  • 优化GitHub网站访问慢的问题

    方法一 修改host文件解决 大型网站服务器都不会是只有一台服务器 而是多台服务器组成的集群一起对外提供服务 使用站长工具测速 找一个速度比较快的服务器 图中可以看到140 82 121 4这个ip比较快 下面修改hosts Mac 在 e
  • Java 学生成绩管理系统 带详细设计报告 功能非常齐全 完整源码

    今天为大家分享一个java语言编写的学生成绩管理系统 目前系统功能已经很全面 后续会进一步完善 整个系统界面漂亮 有完整得源码 希望大家可以喜欢 喜欢的帮忙点赞和关注 一起编程 一起进步 开发环境 开发语言为Java 开发环境Eclipse
  • python实现选择排序

    排序算法 python实现基数排序 python实现归并排序 python实现交换排序 python实现选择排序 python实现插入排序 简单选择排序 基本思想 假设排序表为L 1 n 第i趟排序即从L i n 中选择关键字最小的的元素与
  • TensorFlow实现VGGNet网络模型

    1 VGGNet简介 VGGNet是牛津大学计算机视觉组和Google DeepMind公司的研究员一起研发的深度卷积神经网络 VGGNet探索了卷积神经网络的深度与其性能之间的关系 反复使用33的小型卷积核和22的最大池化层来构筑卷积神经
  • Pycharm创建项目时,解释器如何选择

    最近开始看深度学习 需要用到python 然后就安装了python Anaconda 还有编译IDE pycharm 给大家理一下关系 python就是一个解释器 用来解释程序用的 可以理解为普通C或者C语言的IDE环境 有gcc编译啊之类
  • 掌握Python的X篇_10+11_if分支语句、else语句、elif语句

    文章目录 1 if关键字及语法 2 语句块的概念 3 else语句 4 elif语句 1 if关键字及语法 基本语法如下 if 条件表达式 条件为True时 要执行的语句 举例 number int input Input an numbe
  • 给大家推荐几个查英语缩写的网站

    1 https www acronymfinder com 2 https www abbreviations com 转载于 https www cnblogs com cnwuchao p 10562539 html
  • 实验15:20211127 Java大数据1+X 中级实操考试(id:2660)

    实验15 20211127 Java大数据1 X 中级实操考试 id 2660 一 项目背景说明 二 表结构 三 步骤 5 分 步骤 1 项目准备 5 分 步骤 2 完成实体类 Student 10 分 步骤 3 完成实体类 Course
  • 构造函数不能被继承

    1 派生类不能继承基类的构造函数 必需提供自个的构造函数 防止紧耦合 继承一切成员 构造函数除外 2 析构函数同理 只是由于析构函数无参数需传递 所以似乎能合适的使用 逻辑上 提供 3 由系统隐式使用析构函数 缺省构造函数 class B
  • ubuntu Linux操作系统使用教程(学习笔记)

    本文为 Ubuntu Linux操作系统使用教程 人民邮电出版社 的个人学习笔记 第一章 系统介绍 Linux GNU GPL的关系 Linux的主要发行版本 Linux系统特性及与Windows的区别 略 第二章 系统部署 系统安装 略
  • 模板模式

    简介 在模板模式中 一个抽象类公开定义了执行它的方法的方式 模板 它的子类可以按需要重写方法实现 但调用将以抽象类中定义的方式进行 这种类型的设计模式属于行为型模式 主要解决 一些方法通用 却在每一个子类都重新写了这一方法 注意 一般模板方
  • Spring Bean (作用域,依赖注入四种方式,生命周期)

    附带Spring5 种不同方式的自动装配 一 Spring Bean 作用域 Spring 3 中为 Bean 定义了 5 中作用域 分别为 singleton 单例 prototype 原型 request session 和 globa
  • Mysql安装后登陆失败,(用户名/密码正确)

    使用 AppServ安装Mysql套件 安装完成后使用正确的用户名密码登陆MySQL 错误提示 提示用户名或密码不正确 解决方法 1 安装目录下找到my ini 2 修改my ini 在最后一行加入 skip grant tables 3
  • Postgresql杂谈 20—详解Postgresql中的Checkpoint、WAL日志和热备份恢复

    本文中 我们共同学习下Postgresql的WAL日志 WAL 是Write Ahead Log的简称 翻译过来就是预写日志 或者叫做重做日志 相信大家对数据库事务的四大特性ACID 原子性 一致性 隔离性和持久性 已经非常熟悉了 今天我们