Mysql 学习(十五)redo 日志

news2024/10/7 19:24:59

redo 日志

  • 什么是redo日志?
  • 在说这个之前我们先来想一个场景,在访问磁盘的页面之前,我们会先把页面缓存到Buffer Pool之后,才会访问。写页面的时候也会先将buffer pool中的页面修改之后,然后在某个时机才会刷新到磁盘中。这个时候就有个问题,我们知道 InnoDB 是支持事务的,假设我们提交了一个修改的事务,但是事务提交之后,系统突然发生故障了,导致内存数据丢失了,那我们事务的持久性要怎么做保证呢?
  • 最简单的做法是,事务提交完成前,就把事务所修改的页面都刷新到磁盘,这样就不会有问题
  • 但是,有两个问题:
    • 刷新一个完整的数据页太浪费资源,因为有可能一个数据页只更新了一个数据,然后就要刷新整个数据页,性价比太低了
    • 事务可能包含了很多修改语句,语句可能修改了很多页面,这些页面不一定是顺序存储的,所以我们随机IO 刷新数据页会特别慢
  • 所以上面那种方案性价比太低了,回顾整个场景,我们其实要解决的问题无非就是把修改的操作记录下来,并且事务提交之后永久生效嘛,即使系统崩溃了也能及时修复
  • 而存储事务对数据库修改的日志文件,被称之为 redo log
  • 使用 redo 日志的优点在于:
    • redo 日志占用空间小:
    • redo 日志刷新磁盘是顺序IO

redo 日志格式

  • redo 日志的本质是记录了一下事务对数据库做了哪些修改,所以 redo 日志都会有下面这些通用的结构:在这里插入图片描述

    • type:该条redo日志类型

      • MLOG_1BYTE(type字段对应的十进制数字为1):表示在页面的某个偏移量处写入1个字节的redo日志类型。

      • MLOG_2BYTE(type字段对应的十进制数字为2):表示在页面的某个偏移量处写入2个字节的redo日志类型。

      • MLOG_4BYTE(type字段对应的十进制数字为4):表示在页面的某个偏移量处写入4个字节的redo日志类型。

      • MLOG_8BYTE(type字段对应的十进制数字为8):表示在页面的某个偏移量处写入8个字节的redo日志类型。

        • 当 type 类型为这个类型时,会多一个参数:offset 在这里插入图片描述
      • MLOG_WRITE_STRING(type字段对应的十进制数字为30):表示在页面的某个偏移量处写入一串数据。

        • 当 type 类型为这个类型时,会多两个参数:offset len 在这里插入图片描述
    • space ID:表空间ID

    • page number:页号

    • data:该条redo 日志的具体内容

复杂的 redo 日志类型

  • 有时候执行一条语句会修改很多东西,比如一条insert 语句会更新许多 B+ 树,对于一颗 B+ 树可能又会更新很多节点
  • 那这个时候就有个疑问了,假设我们执行一条语句,会更新很多地方,比如下图: 在这里插入图片描述
  • 我们需不需要把这些更新信息都保存下来呢?毕竟把一条记录插入到一个页面需要更改的地方存储下来的空间可能比单纯存这条记录都要大,所以要怎么存储才合适呢?
  • 这个时候我们就需要引入一些新的 type 类型,这些 type 类型又有配套的 函数,而我们只需要存储这些函数需要的参数就可以了
  • 我们举个例子,类型为 MLOG_COMP_REC_INSERT ,代表插入一条使用紧凑行格式的记录
  • 先来看一下这个日志类型的结构:在这里插入图片描述
    • 其中 n_uniques 代表这条记录的唯一值,field1_len ~ fieldn_len代表着该记录若干个字段占用存储空间的大小,offset代表的是该记录的前一条记录在页面中的地址等等,根据这些参数,在恢复的时候调用这个函数,就可以将数据恢复到系统崩溃前的样子

Mini-Transaction

  • redo 日志的更新是根据组来更新的,而这种方式被称之为 Mini-Transaction ,为什么会需要这样的呢?
  • 这个时候就不得不说一个场景,当你往一个有空闲空间的页插入一条数据,更改的redo记录会比较少,我们基本上使用一行就能解决,叫做乐观插入,但是假设你往一个已经满空间的页插入一条数据,则会产生页分裂,也就是新建一个叶子节点,然后把原先数据页中的一部分记录复制到这个新的数据页中,然后再把记录插入进去,把这个叶子节点插入到叶子节点链表中,最后还要在内节点中添加一条目录项记录指向这个新创建的页面。
  • 很明显上述操作会产生多条redo日志,但是这个操作必须是原子性的,不能说插入一般就停止了,所以就规定执行这些需要保证原子性的操作时,就必须以组的形式来记录redo日志
  • 理解之后,我们就来思考,如何把这些redo日志划分到一个组里边呢?
  • 设计InnoDB的大佬做了一个很简单的小把戏,就是在该组中的最后一条redo日志后边加上一条特殊类型的redo日志,该类型名称为MLOG_MULTI_REC_END,type字段对应的十进制数字为31,该类型的redo日志结构很简单,只有一个type字段:在这里插入图片描述所以某个需要保证原子性的操作产生的一系列redo日志必须要以一个类型为MLOG_MULTI_REC_END结尾,就像这样:在这里插入图片描述这样在系统奔溃重启进行恢复时,只有当解析到类型为MLOG_MULTI_REC_END的redo日志,才认为解析到了一组完整的redo日志,才会进行恢复。否则的话直接放弃前面解析到的redo日志。
  • 但是有些原子性操作只生成了一条redo日志,后面如果加上一个MLOG_MULTI_REC_END的redo日志,会不会太过浪费了,所以设计InnoDB的大佬type的第一个比特位作为判断,如果type字段的第一个比特位为1,代表该需要保证原子性的操作只产生了单一的一条redo日志,否则表示该需要保证原子性的操作产生了一系列的redo日志。
  • 了解完大致之后,我们需要知道,什么时机下产生的redo日志被设计InnoDB的大佬人为的划分成了若干个不可分割的组:
    • 更新Max Row ID属性时产生的redo日志是不可分割的。
    • 向聚簇索引对应B+树的页面中插入一条记录时产生的redo日志是不可分割的。
    • 向某个二级索引对应B+树的页面中插入一条记录时产生的redo日志是不可分割的
    • 还有其他的一些对页面的访问操作时产生的redo日志是不可分割的
  • 顺便总结一下 Mini-Transaction 的概念:设计MySQL的大佬把对底层页面中的一次原子访问的过程称之为一个Mini-Transaction,简称mtr在这里插入图片描述

redo 日志的写入过程

  • redo log block:redo 日志存储单元,都是一个个页在这里插入图片描述

    • log block header:
      • LOG_BLOCK_HDR_NO:每一个block都有一个大于0的唯一标号,本属性就表示该标号值
      • LOG_BLOCK_HDR_DATA_LEN:表示block中已经使用了多少字节,初始值为12(因为log block body从第12个字节处开始)。随着往block中写入的redo日志越来也多,本属性值也跟着增长。如果log block body已经被全部写满,那么本属性的值被设置为512。
      • LOG_BLOCK_FIRST_REC_GROUP:一条redo日志也可以称之为一条redo日志记录(redo log record),一个mtr会生产多条redo日志记录,这些redo日志记录被称之为一个redo日志记录组(redo log record group)
      • LOG_BLOCK_FIRST_REC_GROUP就代表该block中第一个mtr生成的redo日志记录组的偏移量(其实也就是这个block里第一个mtr生成的第一条redo日志的偏移量)。
      • LOG_BLOCK_CHECKPOINT_NO:表示所谓的checkpoint的序号,checkpoint是我们后续内容的重点,现在先不用清楚它的意思,稍安勿躁。
  • redo 日志缓冲区:设计InnoDB的大佬为了解决磁盘速度过慢的问题而引入了Buffer Pool。同理,写入redo日志时也不能直接直接写到磁盘上,实际上在服务器启动时就向操作系统申请了一大片称之为redo log buffer的连续内存空间,翻译成中文就是redo日志缓冲区,我们也可以简称为log buffer。这片内存空间被划分成若干个连续的redo log block,就像这样:在这里插入图片描述

  • log buffer

redo 日志文件

  • 刷机时机:
    • log buffer 空间不足的时候
    • 事务提交的时候
    • 后台线程每秒异步刷新一次 log buffer 到 redo 日志到磁盘
    • 正常关闭服务器的时候
    • checkpoint 的时候
  • 日志文件组:redo 日志文件在磁盘上是不止一个的,而是以一个文件组的方式出现的,文件名是以ib_logfile为前缀,数字为后缀进行命名的,例如 ib_logfile0,ib_logfile1,默认情况下,会创建这两个文件,但是我们可以通过一些参数来调整文件的数量和大小
    • innodb_log_group_home_dir:该参数指定了redo日志文件所在的目录,默认值就是当前的数据目录
    • innodb_log_file_size:该参数指定了每个redo日志文件的大小,在MySQL 5.7.21这个版本中的默认值为48MB
    • innodb_log_files_in_group:该参数指定redo日志文件的个数,默认值为2,最大值为100。
    • 磁盘上redo日志文件的大小就是 innodb_log_file_size × innodb_log_files_in_group
  • 日志文件格式:被划分为 512 个字节大小的block,其实就是由若干个512字节大小的block组成,分为两部分组成
    • 前2048个字节,存储一些管理信息
      • 前4个block分别为什么:在这里插入图片描述

        • log file header:描述该redo日志文件的一些整体属性:在这里插入图片描述
          • LOG_HEADER_FORMAT 4字节 redo日志的版本,在MySQL 5.7.21中该值永远为1
          • LOG_HEADER_PAD1 4 做字节填充用的,没什么实际意义,忽略~
          • LOG_HEADER_START_LSN 8 标记本redo日志文件开始的LSN值,也就是文件偏移量为2048字节初对应的LSN值(关于什么是LSN我们稍后再看,看不懂的先忽略)。
          • LOG_HEADER_CREATOR 32 一个字符串,标记本redo日志文件的创建者是谁。正常运行时该值为MySQL的版本号,比如:“MySQL 5.7.21”,使用mysqlbackup命令创建的redo日志文件的该值为"ibbackup"和创建时间。
          • LOG_BLOCK_CHECKSUM 4 本block的校验值,所有block都有,我们不关心
        • checkpoint1:记录关于checkpoint的一些属性在这里插入图片描述
          • LOG_CHECKPOINT_NO 8字节 服务器做checkpoint的编号,每做一次checkpoint,该值就加1。
          • LOG_CHECKPOINT_LSN 8字节 服务器做checkpoint结束时对应的LSN值,系统奔溃恢复时将从该值开始。
          • LOG_CHECKPOINT_OFFSET 8字节 上个属性中的LSN值在redo日志文件组中的偏移量
          • LOG_CHECKPOINT_LOG_BUF_SIZE 8字节 服务器在做checkpoint操作时对应的log buffer的大小
          • LOG_BLOCK_CHECKSUM 4字节 本block的校验值,所有block都有,我们不关心
        • checkpoint2:结构和checkpoint1一样
    • 从2048个字节开始,就是用来存储block数据
  • Log Sequeue Number:日志序列号,初始值为8704,随着插入日志一直增长
    • 系统第一次启动后初始化 log buffer 时,就会指向第一个block 的偏移量为12字节的地方,然后lsn就会随之增加:8704+12 = 8716
    • 如果当前待插入的block空间可以容纳即将插入mtr提交的日志,lsn的增长值就应该是 mtr 生成 redo 日志占用的字节数,8716+200 = 8916
    • 如果某个mtr产生的一组redo日志占用的存储空间比较大,也就是待插入的block剩余空闲空间不足以容纳这个mtr提交的日志时,lsn增长的量就是该mtr生成的redo日志占用的字节数加上额外占用的log block header和log block trailer的字节数:8916+1000+122+42 = 9948
    • 每一组由mtr生成的redo日志都有一个唯一的LSN值与其对应,LSN值越小,说明redo日志产生的越早
  • flushed_to_disk_lsn:redo日志是首先写到log buffer中,之后才会被刷新到磁盘上的redo日志文件。所以设计InnoDB的大佬提出了一个称之为buf_next_to_write的全局变量,标记当前log buffer中已经有哪些日志被刷新到磁盘中了。在这里插入图片描述
    • 系统初始化的时候,flushed_to_disk_lsn 的值跟 lsn 是一样的
    • 当有新的redo日志写入到log buffer时,首先lsn的值会增长,但flushed_to_disk_lsn不变,随后随着不断有log buffer中的日志被刷新到磁盘上,flushed_to_disk_lsn的值也跟着增长。如果两者的值相同时,说明log buffer中的所有redo日志都已经刷新到磁盘中了。
  • lsn值和redo日志文件偏移量的对应关系: 因为lsn的值是代表系统写入的redo日志量的一个总和,一个mtr中产生多少日志,lsn的值就增加多少(当然有时候要加上log block header和log block trailer的大小),这样mtr产生的日志写到磁盘中时,很容易计算某一个lsn值在redo日志文件组中的偏移量
  • flush链表中的LSN:
    • 在mtr执行过程中可能修改过的页面加入到Buffer Pool的flush链表
    • flush缓存页中的控制块记录两个关于页面何时修改的属性:
      • oldest_modification:如果某个页面被加载到Buffer Pool后进行第一次修改,那么就将修改该页面的mtr开始时对应的lsn值写入这个属性。
      • newest_modification:每修改一次页面,都会将修改该页面的mtr结束时对应的lsn值写入这个属性。也就是说该属性表示页面最近一次修改后对应的系统lsn值。
    • flush链表中的脏页按照修改发生的时间顺序进行排序,也就是按照oldest_modification代表的LSN值进行排序,被多次更新的页面不会重复插入到flush链表中,但是会更新newest_modification属性的值。

checkpoint

  • 有一个很不幸的事实就是我们的redo日志文件组容量是有限的,我们不得不选择循环使用redo日志文件组中的文件,但是这会造成最后写的redo日志与最开始写的redo日志追尾,这时应该想到:redo日志只是为了系统奔溃后恢复脏页用的,如果对应的脏页已经刷新到了磁盘,也就是说即使现在系统奔溃,那么在重启后也用不着使用redo日志恢复该页面了,所以该redo日志也就没有存在的必要了,那么它占用的磁盘空间就可以被后续的redo日志所重用。也就是说:判断某些redo日志占用的磁盘空间是否可以覆盖的依据就是它对应的脏页是否已经刷新到磁盘里。
  • 这边举个例子来说明:现在页a被刷新到了磁盘,mtr_1生成的redo日志就可以给覆盖了,所以进行一个增加checkpoint_lsn操作
    • 步骤一:计算一下当前系统中可以被覆盖的redo日志对应的lsn值最大是多少:redo日志可以被覆盖,意味着它对应的脏页被刷到了磁盘,只要我们计算出当前系统中被最早修改的脏页对应的oldest_modification值,那凡是在系统lsn值小于该节点的oldest_modification值时产生的redo日志都是可以被覆盖掉的,我们就把该脏页的oldest_modification赋值给checkpoint_lsn。
      • 比方说当前系统中页a已经被刷新到磁盘,那么flush链表的尾节点就是页c,该节点就是当前系统中最早修改的脏页了,它的oldest_modification值为8916,我们就把8916赋值给checkpoint_lsn(也就是说在redo日志对应的lsn值小于8916时就可以被覆盖掉)。
    • 步骤二:将checkpoint_lsn和对应的redo日志文件组偏移量以及此次checkpint的编号写到日志文件的管理信息(就是checkpoint1或者checkpoint2)中。
      • 设计InnoDB的大佬维护了一个目前系统做了多少次checkpoint的变量checkpoint_no,每做一次checkpoint,该变量的值就加1。我们前面说过计算一个lsn值对应的redo日志文件组偏移量是很容易的,所以可以计算得到该checkpoint_lsn在redo日志文件组中对应的偏移量checkpoint_offset,然后把这三个值都写到redo日志文件组的管理信息中。
      • 我们说过,每一个redo日志文件都有2048个字节的管理信息,但是上述关于checkpoint的信息只会被写到日志文件组的第一个日志文件的管理信息中。不过我们是存储到checkpoint1中还是checkpoint2中呢?设计InnoDB的大佬规定,当checkpoint_no的值是偶数时,就写到checkpoint1中,是奇数时,就写到checkpoint2中。

崩溃恢复

确定恢复的起点

  • 从checkpoint_lsn 开始读取redo日志来恢复页面
  • 衡量checkpoint发生时间早晚的信息就是所谓的checkpoint_no,我们只要把checkpoint1和checkpoint2这两个block中的checkpoint_no值读出来比一下大小,哪个的checkpoint_no值更大,说明哪个block存储的就是最近的一次checkpoint信息。这样我们就能拿到最近发生的checkpoint对应的checkpoint_lsn值以及它在redo日志文件组中的偏移量checkpoint_offset。

确定恢复的终点

  • 普通block的log block header部分有一个称之为LOG_BLOCK_HDR_DATA_LEN的属性,该属性值记录了当前block里使用了多少字节的空间。对于被填满的block来说,该值永远为512。如果该属性的值不为512,那么就是它了,它就是此次奔溃恢复中需要扫描的最后一个block。

总结(待更新)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1509272.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Centos7 安装mongodb 7.0

官方手册参考: https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-red-hat/ Mongodb支持的版本 安装 MongoDB 社区版 按照以下步骤使用包管理器安装 MongoDB Community Edition yum。 配置包管理系统 ( yum) 创建一个/etc/yum.repos.d/mongodb-o…

uni-app微信小程序上拉加载,下拉刷新

pages.json配置官网链接 onPullDownRefresh、onReachBottom函数跟生命周期同级 data() {return {orderList:[],total: null, //总共多少条数据page: 1,pageSize: 10,} }, onLoad() {}, mounted(){this.getInfo() }, methods:{getInfo(){API.getListxxx().then(res > {const…

sensitive-word 敏感词 违规文字检测

1、快速开始 - JDK1.7- Maven 3.x 2、Maven 引入 <!-- https://mvnrepository.com/artifact/com.github.houbb/sensitive-word --><dependency><groupId>com.github.houbb</groupId><artifactId>sensitive-word</artifactId><version…

【CSS3】CSS3 3D 转换示例 - 3D 旋转木马 ( @keyframes 规则 定义动画 | 为 盒子模型 应用动画 | 开启透视视图 | 设置 3D 呈现样式 )

文章目录 一、3D 导航栏示例 - 核心要点1、需求分析2、HTML 结构section 标签 3、CSS 样式keyframes 规则 定义动画为 盒子模型 应用动画开启透视视图设置 3D 呈现样式鼠标移动到控件上方效果设置 6 个子盒子模型的效果 二、完整代码示例1、代码示例2、展示效果 一、3D 导航栏示…

安全防御第七次作业

拓扑图如图所示&#xff1a; 问题&#xff1a;在FW7和FW8之间建立一条IPSEC通道保证10.0.2.0/24网段 可以正常访问到192.168.1.0/24 注&#xff1a;基础配置我在此省略了 一、NAT配置 FW4&#xff1a; FW6&#xff1a; 二、在FW4上做服务器映射 三、配置IPSEC FW5&#xff…

最大的单入口空闲区域

最大的单入口空闲区域 问题描述输入输出代码实现 问题描述 找到最大的单入口空闲区域。 空闲区域是由连通的’O’组成的区域&#xff0c;位于边界的’O’可以是入口&#xff0c; 单入口空闲区域即有且只有一个位于边界的’O’作为入口的由连通的’O’组成的区域。 如果两个元素…

SpringBoot中定时任务、corn表达式

SpringBoot中定时任务、corn表达式 corn表达式网站&#xff1a;https://cron.qqe2.com/ 方法上加上Scheduled(cron表达式) 启动类上加上EnableScheduling 示例 启动类上 启动类加上EnableScheduling开启定时任务。 SpringBootApplication EnableScheduling public class…

应用方案 | D54123B低功耗漏电保护电路

概 述 A&#xff09;、D54123B是一款高性能 CMOS 漏电保护器专用电路。芯片内部包含稳压电源、放大电路、比较器电路、延时电路、计数器电路、跳闸控制电路及跳闸驱动电路。芯片外围应用有脱扣线圈、压敏电阻、稳压二级管、二级管、电阻、电容等元器件。 B&#xff09;、内部…

Day35:安全开发-JavaEE应用原生反序列化重写方法链条分析触发类类加载

目录 Java-原生使用-序列化&反序列化 Java-安全问题-重写方法&触发方法 Java-安全问题-可控其他类重写方法 思维导图 Java知识点&#xff1a; 功能&#xff1a;数据库操作&#xff0c;文件操作&#xff0c;序列化数据&#xff0c;身份验证&#xff0c;框架开发&…

数据结构顺序表的操作,窗口界面(c语言版)

// 准备头文件 #include <stdio.h> #include <stdlib.h>#define InitSize 10 // 动态顺序表的初始默认长度// 定义C语言的bool变量 #define bool char #define true 1 #define false 0/* 定义数据元素的数据类型 */ typedef int ElemType; // 方便更改// 动态顺…

职场逆袭!如何打造‘黄金简历’

现在的职场&#xff0c;无论是哪个行业都特别卷&#xff0c;想要找到一份满意&#xff0c;不仅要求你要拥有丰富的工作经验&#xff0c;而且还要求你的简历足够精彩&#xff0c;这样才能在一众求职者中脱颖而出&#xff01; 一、希赛老师在线指导 之前&#xff0c;小赛分享了…

从0到1了解工业物联网,只需掌握这七点!

目录 1、什么是工业物联网&#xff1f; 2、为什么需要工业物联网&#xff1f; 3、工业物联网与物联网的区别&#xff1f; 4、工业物联网与工业互联网的区别&#xff1f; 5、工业物联网有哪些典型特征&#xff1f; 6、工业物联网方案架构&#xff1f; 7、工业物联网有哪些…

用了一个select框出现的问题许多问题差不多搞了一个多小时最后还是百度解决了,百度伟大

问题出现 问题描述 select 多选框里的数据问题&#xff0c;我讲获取的数据信息放入框ref(null) 中&#xff0c;将数据返回到返回框里&#xff0c;一直发现存在问题&#xff0c;不能正常显示&#xff0c;百度里一下&#xff0c;发现没有百度到其他问题&#xff0c;最后换了一种…

【网络安全】-数字证书

数字证书 数字证书是互联网通讯中用于标志通讯各方身份信息的一串数字或数据&#xff0c;它为网络应用提供了一种验证通信实体身份的方式。具体来说&#xff0c;数字证书是由权威的证书授权&#xff08;CA&#xff09;中心签发的&#xff0c;包含公开密钥拥有者信息以及公开密…

29个社媒营销经典案例!外贸人速来学习!

今天给大家分享一些比较经典的外贸社媒营销案例&#xff0c;希望对大家有帮助&#xff01; 01 创建重复的内容系列 如果你每天都在为决定要在社交媒体上发布什么内容而焦头烂额&#xff0c;那就创建一些你擅长的重复内容系列和主题。 例如&#xff0c;有人经常分享鼓舞人心的…

JAVA实战开源项目:校园失物招领管理系统(Vue+SpringBoot)

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 招领管理模块2.2 寻物管理模块2.3 系统公告模块2.4 感谢留言模块 三、界面展示3.1 登录注册3.2 招领模块3.3 寻物模块3.4 公告模块3.5 感谢留言模块3.6 系统基础模块 四、免责说明 一、摘要 1.1 项目介绍 校园失物招领…

Java实现Tron(波场)区块链的开发实践(三)波场链水龙头、WEB3测试实战

上一节我们具体讲到Java实现Tron波场链的逻辑代码实现。 这一节我们通过部署和开发好的代码&#xff0c;针对测试链进行自测开发&#xff0c;准备测试环境。 1. 创建离线地址 首先我们需要一个离线地址&#xff0c;我们不需要在线进行创建&#xff0c;直接可以通过第一节的离…

py脚本模拟json数据,StructuredStreaming接收数据存储HDFS一些小细节 ERROR:‘path‘ is not specified

很多初次接触到StructuredStreaming 应该会写一个这样的案例 - py脚本不断产生数据写入linux本地&#xff0c; 通过hdfs dfs 建目录文件来实时存储到HDFS中 1. 指定数据schema&#xff1a; 实时json数据 2. 数据源地址&#xff1a;HDFS 3. 结果落地位置&#xff1a; HDFS …

PostgreSQL索引篇 | Hash索引

PostgreSQL版本为8.4.1 &#xff08;本文为《PostgreSQL数据库内核分析》一书的总结笔记&#xff0c;需要电子版的可私信我&#xff09; 索引篇&#xff1a; PostgreSQL索引篇 | BTreePostgreSQL索引篇 | GIN索引PostgreSQL索引篇 | Gist索引PostgreSQL索引篇 | TSearch2 全文…

哪里下载短视频素材?推荐几个短视频素材下载网站

当短视频行业的迅速崛起&#xff0c;剪辑影视短片的魅力无法抗拒&#xff0c;越来越多朋友爱看短视频&#xff0c;但从哪里找到高清、无水印和无字幕的短视频素材呢&#xff1f;今天&#xff0c;我将为大家推荐几个可获取短视频素材的优秀网站&#xff0c;下面让我们一起去看看…