mysql系列7—Innodb的redolog

背景

本文涉及的内容较为底层,做了解即可,是以前学习《高性能Mysql》和《mysql是怎样运行的》的笔记整理所得。

redolog(后续使用redo日志表示)的核心作用是保证数据库的持久性。
在mysql系列5—Innodb的缓存中介绍过:数据和索引保存在磁盘上,为提高效率读写时需从磁盘将数据加载到内存(Buffer Pool)中,并基于内存进行读写。内存中的数据不稳定,当系统断电或者崩溃时数据会丢失,因此所有修改最终都要刷入磁盘。
数据库事务具有持久性:事务提交成功后,无论数据库环境如何(数据库或者操作系统崩溃),已提交事务涉及的修改都会被保存下来。
最简单的实现方法是每次事务提交都将Buffer Pool中的脏页刷入磁盘,然后返回事务提交成功。这意味着每次事务提交时都进行刷盘,会带来如下三个问题:
[1] 刷盘相比内存操作的速度慢太多,会严重影响mysql整体性能;
[2] SQL可能只修改了一个字节,然而需要以页为单位进行刷盘;
[3] SQL可能影响多行记录,这些行可以位于不同的页中,涉及随机IO,效率较低。
mysql引入redo日志为其提供了一个解决方案。redo日志通过特殊的格式设计从而占据较小的空间,且redo日志的顺序写入相对随机IO效率较高。因此,相对于直接将更新刷入磁盘,将修改记录(redo日志)刷入磁盘可以在保证数据库持久性的前提下最大可能降低对数据库性能的影响。

1.redo文件介绍

本章先从整体上对redo文件的格式进行认识,为后续章节作准备。内容包括对redo日志文件组、文件格式、块和每条redo日志格式的介绍,泛泛了解即可。
在这里插入图片描述
整体结构图如上所示,以下分别进行介绍。

1.1 redo文件组

redo文件以文件组的形式存在,文件组首尾串联。先写向第一个文件,写满后再转向下一个,循环复用,如下图所示:
在这里插入图片描述
redo日志文件组的实现在mysql8和mysql5中有所区别。

mysql5

(1) 存放路径
由innodb_log_group_home_dir变量确定日志的存储路径,默认与mysql的数据目录同一路径;

mysql> SHOW VARIABLES LIKE 'innodb_log_group_home_dir';
+---------------------------+-------+
| Variable_name             | Value |
+---------------------------+-------+
| innodb_log_group_home_dir | ./    |
+---------------------------+-------+

(2) 文件个数
由innodb_log_files_in_group变量确定文件组中文件的个数,默认为2;

mysql> SHOW VARIABLES LIKE 'innodb_log_files_in_group';
+---------------------------+-------+
| Variable_name             | Value |
+---------------------------+-------+
| innodb_log_files_in_group | 2     |
+---------------------------+-------+

(3) 文件大小
由innodb_log_file_size变量确定每个文件的大小,默认为50331648比特,即48M.

mysql> SHOW VARIABLES LIKE 'innodb_log_file_size';
+----------------------+----------+
| Variable_name        | Value    |
+----------------------+----------+
| innodb_log_file_size | 50331648 |
+----------------------+----------+

进入对应目录查询实际文件:

[root@124 mysql]# cd /var/lib/mysql
[root@124 mysql]# ls -al | grep ib_logfile
-rw-r-----.  1 mysql mysql  50331648 1215 09:46 ib_logfile0
-rw-r-----.  1 mysql mysql  50331648 1029 00:00 ib_logfile1

mysql8

(1) 存放路径:
存放路径位于datadir的#innodb_redo文件夹相下,即默认为/var/lib/mysql/#innodb_redo.

(2) 文件个数和文件大小
由变量innodb_redo_log_capacity控制文件组的整体大小,默认为100M;文件个数固定为32个,因此每个文件大小为3276800比特.

[root@localhost #innodb_redo]# cd /var/lib/mysql/#innodb_redo
[root@localhost #innodb_redo]# ll -t
总用量 102400
-rw-r-----. 1 mysql mysql 3276800 1215 10:11 #ib_redo13030
-rw-r-----. 1 mysql mysql 3276800 1212 16:29 #ib_redo13029
-rw-r-----. 1 mysql mysql 3276800 1211 07:31 #ib_redo13028
-rw-r-----. 1 mysql mysql 3276800 125 17:19 #ib_redo13059_tmp
-rw-r-----. 1 mysql mysql 3276800 125 17:19 #ib_redo13058_tmp
-rw-r-----. 1 mysql mysql 3276800 125 17:19 #ib_redo13057_tmp
...
-rw-r-----. 1 mysql mysql 3276800 125 17:19 #ib_redo13033_tmp
-rw-r-----. 1 mysql mysql 3276800 125 17:19 #ib_redo13032_tmp
-rw-r-----. 1 mysql mysql 3276800 125 17:19 #ib_redo13031_tmp

1.2 redo文件和redo块

对文件组有概念后,再看一下redo文件。
每个redo文件由两部分组成: 文件头+数据部分;
在这里插入图片描述
文件头占据2048个字节,存储当前redo文件的管理信息;数据部分存储redo日志。
redo文件也以页为单位进行管理,不过一个redo页占据512K, 后面用log block表示(与前面介绍的数据页进行区分)。其中文件头占据前4个log block, 数据部分由多个log block(redo页)组成,数据部分用于存放实际的redo数据。
再看一下log block的结构,由头部、主体和尾部组成, 如下图所示:
在这里插入图片描述
header和tailer存储log block的管理信息和校验信息,body中存放redo数据。
其中,header字段中有两个属性比较重要:
[1] log_block_hdr_no: 表示log block的唯一ID;
[2] log_block_hdr_data_len: 表示当前log block已使用了多少字节;初始值为12(header长度),当496个body全部写完后,log_block_hdr_data_len为512.[后面用到]
还有个地方需要注意一下(可先跳过,看第5章时再折回):
尽管redo日志文件组的每个文件都有一个文件头且占据了2048个字节的管理信息;仅第一个文件管理信息中使用两个log block(checkpoint1和chekpoint2)记录了checkpoint信息,包括checkpoint_no(checkpoint编号)和checkpoint_lsn(checkpoint时的lsn); 每次checkpoint时,checkpoint_no会递增,根据奇偶性依次写入checkpoint1和checkpoint2,以防止某次写错导致数据无法恢复。[后面用到]

1.3 redo日志格式

每条redo日志包含的信息有"在哪个表空间的哪个页上做什么",如: “将第10号表空间的100号页面的偏移量为1000处的值更新为10000”。
redo日志的格式设计如下:
在这里插入图片描述
[1] type表示redo日志的类型,决定了data的数据组成和解析过程,type字段主要是为了节省空间;
[2] spaceId和pageId表示表空间ID和数据页ID,用于定位数据变更发生的的位置;
[3] data记录改动的具体内容。

2.mini-transaction单元

上述redo日志格式中包含了表空间、页序号、修改数据等信息,记录了一个修改操作的所有信息。mini-transaction(以下用mtr表示)是将一组相关联的redo日志组合成一个组,mysql将一个mtr设计为一个原子操作。mtr的原子性设计为redo日志的持久化功能提供了基础,以下结合案例进行说明。
假设B+树索引的页A、B、C位于同一个表空间(tablespace001),页B比较空闲(只有一条记录),当插入一行主键为50的记录时,索引树的变化如下图所示:
在这里插入图片描述
此时涉及一个改动,在tablespace001的页B中添加一条记录50;对应的redo日志只有一条记录,近似表示为:

在tablespace001表空间的页B中添加一条记录50;

然而,如果页b已存满记录,50通过计算需要存储在页b中,此时需要经历页裂过程:
在这里插入图片描述
将页B裂解为页B和页D,涉及的步骤有:
[1] 创建一个页D;
[2] 将页B的51和90记录前移到页D;
[3] 在页A中新增一个目录记录项,指向页D;
[4] 将记录50添加到页B中.
此时,对应的redo日志有多条记录, 近似表示为:

在tablespace001表空间创建一个页D;
在tablespace001表空间的页A中添加一个目录项指向页D;
向tablespace001表空间的页D中添加记录51;
向tablespace001表空间的页D中添加记录90;
删除tablespace001表空间的页B的51记录;
删除tablespace001表空间的页B的90记录;
修改tablespace001表空间中页B的后驱节点为页D;
修改tablespace001表空间中页D的前驱节点为页B;
修改tablespace001表空间中页C的前驱节点为页D;
修改tablespace001表空间中页D的后驱节点为页C;
在tablespace001表空间的页B中添加一条记录50;
...
# 此外,此时页A页的空间也不足够添加新的目录项记录,则对页A需要进行裂解,页A如果还有上级... 

插入一条记录对一个索引树的修改对应多条记录,这些redo日志需要具备原子性以保证B+树的正确性,如不能仅仅完成了页的裂解而没有拷贝数据,不能仅创建了叶子节点而未在父节点新增目录项记录等。
上述这些redo日志被标记为一个mtr,一个mtr内的redo日志必须完整。mtr的设计为日志的崩溃后恢复提供了较好的设计基础,将在第5章中介绍。
上述一条INSERT语句仅仅是针对一个索引树,一般一个表有多个索引,因此一条INSERT语句一般对应多个mtr, 如自增主键也是一个mtr. 所以可用下图表示SQL与mtr与redo日志的关系:
在这里插入图片描述

3.log buffer

与Buffer Pool类似,mysql引入log buffer作为redo日志的内存缓存以加快IO速度、提高系统性能。mysql启动时在内存中划出一块区域用于缓存redo日志,这部分区域叫做log buffer. log buffer也是以log block为单元进行管理, 即redo日志先写入到log buffer再刷新到磁盘。
log buffer内存大小由innodb_log_buffer_size变量确定,默认为16777216比特,即16M.

mysql> SHOW VARIABLES LIKE 'innodb_log_buffer_size';
+------------------------+----------+
| Variable_name          | Value    |
+------------------------+----------+
| innodb_log_buffer_size | 16777216 |
+------------------------+----------+

对于log buffer的理解可以从两个角度进行,数据写入到log buffer以及log buffer刷盘到redo日志文件; 将redo日志写到缓冲区是以mtr为单元进行,而将缓存写到磁盘是以redo页(log block)为单位进行。

3.1 写入log buffer

mysql将redo日志写入log buffer是以mtr为单位进行,以下结合案例介绍写入过程。
假设有两个事物A和B,事务A和B分别包括两个mtr, 按照时间的先后的执行顺序依次为mtr-a-1, mtr-b-1, mtr-a-2,mtr-a-2:
在这里插入图片描述
将mtr写入log buffer的流程图如下所示:
在这里插入图片描述
说明:每个mtr都有一个唯一标识lsn,即每个mtr内的所有redo日志具有相同的lsn。

3.2 log buffer写入磁盘

考虑到机器断电异常会导致内存数据丢失,而redo日志本身就是为了保证数据库事务的持久性而引入,因此何时对log buffer进行刷盘很重要。涉及到什么时候将log buffer刷入到磁盘。
在这里插入图片描述
log buffer的刷盘时机有以下几种:
[1] log buffer空间不足时(占据了总容量的一半),需要刷盘以预留出足够的内存空间缓存新的redo日志;
[2] 事务提交时刷盘;
[3] 后台线程每隔1秒触发一次刷盘;
[4] 正常关闭服务器时,为了保证redo日志持久化,需要刷盘;
[5] checkpoint时,将在第四章中介绍;
其中,当事务提交时立即刷盘log buffer,刷盘完成后上报事务提交成功,保证了redo日志的持久性。
根据业务对持久性的要求不同,mysql提供了一个调优参数innodb_flush_log_at_trx_commit.

mysql> SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1     |
+--------------------------------+-------+

[1] innodb_flush_log_at_trx_commit默认为1,表示事务提交时立即将事务涉及的log buffer刷盘,确保了事务的持久性;
[2] 0表示不主动刷盘,由后台线程异步刷盘(1s一次),如果数据库宕机会导致部分数据丢失;
[3] 2表示将输入拷贝到操作系统缓冲区,由操作系统刷盘; 数据库宕机不影响数据的持久性,但操作系统宕机会导致数据丢失。
本文后续以默认的刷盘机制(innodb_flush_log_at_trx_commit=1)进行介绍。

3.3 log buffer清理

在理解log buffer清理问题前,需要先理解两个变量: lsn和flushed_to_disk_lsn。
lsn是个全局变量,表示日志序列号(Log Sequeue Number),用于记录生成的redo日志量,从8704开始不断递增。由于redo日志是以mtr为单位写入log buffer的,所以整个mtr内的redo日志具有相同的lsn;不同的mtr具备不同的lsn, 且lsn越小对表示redo日志生成的越早。
在这里插入图片描述
如上图所示,mtr1对应的lsn是8704, mtr2对应的日志是8804(9704+100),下一个mtr的lsn对应8954(8804+150),后续lsn取决于上一个mtr的长度。
flushed_to_disk_lsn表示已被刷新到磁盘的lsn,即lsn小于flushed_to_disk_lsn的redo日志已经被刷入磁盘,这部分log buffer内存可被清理:
在这里插入图片描述
图中最左侧的log buffer区域可被清理和重复利用。当flushed_to_disk_lsn与LSN相同时,表示所有的redo日志已刷入磁盘。
说明:flushed_to_disk_lsn表示的是log buffer日志被刷入到磁盘,还有一个write_lsn表示记录刷到了操作系统缓存。

4.redo日志文件清理

前面已经介绍了redo日志写入log buffer以及log buffer刷入磁盘的过程。flushed_to_disk_lsn变量表示已经写到磁盘的redo日志量,lsn超出flushed_to_disk_lsn的redo日志可以从log buffer中被清理出。
本章需要思考对于已刷入磁盘的redo日志何时可以被清理。redo日志文件整体大小是固定的,且redo日志组会循环使用;因此需要一个合理的清理机制,并需要考虑两个问题:
[1] 确认哪些redo日志可以被清理,数据被清理后不能影响持久性功能;
[2] 何时清理,需要有足够的空间保障log buffer的redo日志记录可以被刷入redo日志文件。

flush链表和lsn

对于flush链表的介绍在[mysql系列6—Innodb的缓存]中已详细介绍。

修改数据库时,页的改动信息会以redo日志的形式记录下,然后将涉及的改动页对应的控制块添加到flush链表中,控制块之间形成双向链表。
flush链的控制块中保存了两个修改信息oldest_modification和newest_modification,分别表示第一次修改时的lsn和最近一次修改结束后的lsn.
使用第三章中的案例,从mysql启动后,分别执行了两个mtr, mtr1占据100字节,mtr2占据150字节:
在这里插入图片描述
假设mtr1修改了两个页面,两个页面对应的控制块分别为"控制块1"和"控制块2";mtr2修改了一个页面,页面控制块为"控制块3", 则flush链表可以表示为:
在这里插入图片描述
图中O_M表示oldest_modification, N_M表示newest_modification.
页面被修改时,页面对应的控制块会加入flush链的队列头部,oldest_modification记录下当时的lsn, newest_modification会记录所在mtr结束时的lsn; 当后续这个页面再次被修改时,仅修改newest_modification的值,oldest_modification和位置不会发生变化,即控制块是按照第一次修改的时间按从大到小的顺序排列的。
因此,位于队列尾部的控制块的oldest_modification是整个flush链中最小的lsn(后续用最小LSN表示).
另外,当脏页(修改过的数据页)被写入到磁盘后,数据页对应的控制块会从flush链中删除,因此redo日志文件中lsn小于**"最小LSN"可以被直接删除。
由此可以得出一个结论:判断redo日志文件中日志是否可被删除的条件是,脏页是否已经刷到磁盘了,即redo日志的lsn是否小于
最小LSN**。
由于全局LSN根据写入log buffer的mtr大小进行递增,且log buffer与redo日志文件以log block为单位进行存储;因此可根据lsn计算出对应redo日志在文件组中的偏移量。

checkpoint

上一节提到了一个**“最小LSN"的概念,mysql使用checkpiont_lsn表示。刷新一次checkpiont_lsn的操作被称为一次chekpoint操作,会计算出当前系统的最小LSN并赋值给checkpiont_lsn:
[1] 取出redo文件组的第一个文件的管理信息, 得到较大的checkpoint_no和对应的checkpoint_lsn;
[2] 对checkpoint_no加1,并写入文件;
[3] 将当前
"最小LSN”**赋值给checkpoint_lsn,并写入到文件中。
经历checkpoint后,redo日志文件中可被清理的部分会被标记出,清理操作由后台线程完成。

5.崩溃恢复

从崩溃中恢复是redo日志保证持久性的关键所在。数据库崩溃后,根据redo日志进行数据库恢复可分为三个步骤:确定恢复起点、确定恢复终点、执行恢复流程。

5.1 确认恢复起始点

章节1.2中介绍过,redo日志文件组中第一个文件的管理信息中在两个block都存储了checkpoint_no和checkpoint_lsn的信息。其中较大的checkpoint_lsn表示上一次checkpoint操作对应的lsn,即最小LSN.
由于lsn小于checkpoint_lsn的脏页已经被刷盘,可通过checkpoint_lsn可以计算出在redo日志组中的偏移量,使用该偏移量作为数据恢复的起点。
在这里插入图片描述
上图中控制块3表示未刷盘的最早被修改的页的控制块,其oldest_modification是最小的lsn, 用作数据恢复的起点。

5.2 确认恢复终点

章节1.2中介绍过每个log block的header存储了log_block_hdr_data_len属性,记录当前页使用的字节数。如果log_block_hdr_data_len等于512,说明当前页已满,小于512说明当前页未满,未满的block表示最新的数据。通过未满的block的log_block_hdr_no可计算出在redo日志组中的偏移量,作为数据恢复的终点。
在这里插入图片描述
上图中redo日志文件的第100个log block已满,而第101个未满,101最后一个redo日志的lsn作为日志恢复终点的lsn.

5.3 恢复流程

恢复的时候需要注意一个问题:checkpoint_lsn之前的redo日志对应的脏页已经刷入磁盘了,但checkpoint_lsn之后的可能已刷盘也可能没有,需要区分对待。
每个数据页都有一个File Header文件头,通过file_page_lsn属性记录了最近一次修改页面时的lsn值(对应flush链中的newest_modification)。在checkpoint之后刷入磁盘的脏页,file_page_lsn大于checkpoint_lsn;对于这部分数据页,恢复时直接跳过;file_page_lsn和数据页的内容可参考mysql系列5—Innodb的缓存。
通过redo日志组的恢复起点偏移量和终点偏移量可以得到一段redo日志,再根据file_page_lsn过滤掉一部分,得到一个redo日志数组。
这些redo日志数组可能涉及不同表空间的不同数据页的修改操作,如redo-1日志修改表空间1-数据页1,redo-2修改表空间100-数据页100,redo-3修改表空间1-数据页1,redo-4修改表空间100-数据页100,… 随机写的概率比较大,可根据(表空间ID, 数据页页号)通过哈希算法对redo日志进行映射分组,然后按照组依次执行,将随机IO转为多个连续IO从而提高系统效率。

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

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

相关文章

C++【内存管理】

C/C中程序的内存划分: 栈:又称堆栈,存放非静态的局部变量、函数参数、返回值等等,栈是向下增长的。内存映射段:是高效的I/O映射方式,用于装载一个共享的动态内存库。用户…

手机租赁平台开发助力智能设备租赁新模式

内容概要 手机租赁平台开发,简单说就是让你用得起高大上的智能设备,不管是最新款的手机、平板,还是那些炫酷的智能耳机,这个平台应有尽有。想要体验但又不希望花大钱?那你就找对地方了!通过灵活的租赁方案…

【开源免费】基于SpringBoot+Vue.JS校园社团信息管理系统(JAVA毕业设计)

本文项目编号 T 107 ,文末自助获取源码 \color{red}{T107,文末自助获取源码} T107,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

【鸿蒙NEXT】鸿蒙里面类似iOS的Keychain——关键资产(@ohos.security.asset)实现设备唯一标识

前言 在iOS开发中Keychain 是一个非常安全的存储系统,用于保存敏感信息,如密码、证书、密钥等。与 NSUserDefaults 或文件系统不同,Keychain 提供了更高的安全性,因为它对数据进行了加密,并且只有经过授权的应用程序才…

使用npm包的工程如何引入mapboxgl-enhance/maplibre-gl-enhance扩展包

作者:刘大 前言 在使用iClient for MapboxGL/MapLibreGL项目开发中,往往会对接非EPSG:3857坐标系的地图,由于默认不支持,因此需引入mapboxgl-enhance/maplibre-gl-enhance扩展包。 在使用Vue等其他框架,通过npm包下载…

应急指挥系统总体架构方案

引言 应急指挥系统总体架构方案旨在构建一个高效、智能的应急管理体系,以应对自然灾害、事故灾难等突发事件,保障人民生命财产安全。 背景与挑战 近年来,安全生产形势严峻,自然灾害事故频发,对应急指挥系统的要求越…

如何用CSS3创建圆角矩形并居中显示?

在网页设计中,圆角矩形因其美观和现代感而被广泛使用,居中显示元素也是一个常见的需求。今天,我们将学习如何使用CSS3的border-radius属性来创建圆角矩形,并将其居中显示在页面上。 如果你正在学习CSS,那么这个实例将非…

UE5通过蓝图节点控制材质参数

通过蓝图节点控制材质的参数 蓝图节点 在材质上设置标量值 和 在材质上设置向量参数值 Set Scalar Parameter Value on Materials Set Vector Parameter Value on Materials 这两个蓝图节点都可以在蓝图中,控制材质的参数值和向量值

canvas+fabric实现时间刻度尺(二)

前言 我们前面实现了时间刻度尺&#xff0c;鼠标移动显示时间&#xff0c;接下来我们实现鼠标点击某个时间进行弹框。 效果 实现 1.监听鼠标按下事件 2.编写弹框页面 3.时间转换 <template><div><canvas id"rulerCanvas" width"1200"…

手机实时提取SIM卡打电话的信令声音-双卡手机来电如何获取哪一个卡的来电

手机实时提取SIM卡打电话的信令声音 --双卡手机来电如何获取哪一个卡的来电 一、前言 前面的篇章《手机实时提取SIM卡打电话的信令声音-智能拨号器的双SIM卡切换方案》中&#xff0c;我们论述了局域网SIP坐席通过手机外呼出去时&#xff0c;手机中主副卡的呼叫调度策略。 但…

离线语音识别+青云客语音机器人(幼儿园级别教程)

1、使用步骤 确保已安装以下库&#xff1a; pip install vosk sounddevice requests pyttsx3 2、下载 Vosk 模型&#xff1a; 下载适合的中文模型&#xff0c;如 vosk-model-small-cn-0.22。 下载地址&#xff1a; https://alphacephei.com/vosk/models 将模型解压后放置在…

Streaming Dense Video Captioning

原文出处 CVPR 原文链接 [2404.01297] Streaming Dense Video Captioninghttps://arxiv.org/abs/2404.01297 原文笔记 What 1、提出了一种基于聚类传入token的新记忆模块&#xff0c;该模块可以处理任意长的视频&#xff0c;并且可以在不访问视频所有帧的情况下处理视频(…

TCP 连接:三次握手与四次挥手

TCP 协议&#xff0c;全称为“传输控制协议”。 1. TCP 协议段格式 给出几个定义 &#xff1a; 16位源端口号 &#xff1a;用于标识发送端的应用程序。 16位目的端口号 &#xff1a;用于标识接收端的目标应用程序。 32位序号 &#xff1a;用于标识发送的每一个字节流中的第一…

IDEA+Docker一键部署项目SpringBoot项目

文章目录 1. 部署项目的传统方式2. 前置工作3. SSH配置4. 连接Docker守护进程5. 创建简单的SpringBoot应用程序6. 编写Dockerfile文件7. 配置远程部署 7.1 创建配置7.2 绑定端口7.3 添加执行前要运行的任务 8. 部署项目9. 开放防火墙的 11020 端口10. 访问项目11. 可能遇到的问…

redis开发与运维-redis0401-补充-redis流水线与Jedis执行流水线

文章目录 【README】【1】redis流水线Pipeline【1.1】redis流水线概念【1.2】redis流水线性能测试【1.2.1】使用流水线与未使用流水线的性能对比【1.2.2】使用流水线与redis原生批量命令的性能对比【1.2.3】流水线缺点 【1.3】Jedis客户端执行流水线【1.3.1】Jedis客户端执行流…

Uncaught ReferenceError: __VUE_HMR_RUNTIME__ is not defined

Syntax Error: Error: vitejs/plugin-vue requires vue (>3.2.13) or vue/compiler-sfc to be present in the dependency tree. 第一步 npm install vue/compiler-sfc npm run dev 运行成功&#xff0c;本地打开页面是空白&#xff0c;控制台报错 重新下载了vue-loa…

【微服务】【Sentinel】认识Sentinel

文章目录 1. 雪崩问题2. 解决方案3. 服务保护技术对比4. 安装 Sentinel4.1 启动控制台4.2 客户端接入控制台 参考资料: 1. 雪崩问题 微服务调用链路中的某个服务故障&#xff0c;引起整个链路中的所有微服务都不可用&#xff0c;这就是雪崩。动图演示&#xff1a; 在微服务系统…

STM32完全学习——使用定时器1精确延时

一、定时器的相关配置 首先一定要是递减定时器&#xff0c;递增的不太行&#xff0c;控制的不够准确&#xff0c;其次在大于10微秒的延时是非常准确的&#xff0c;小于的话&#xff0c;就没有那没准&#xff0c;但是凑合能用。误差都在一个微秒以内。使用高级定时器也就是时钟…

connect to host github.com port 22: Connection timed out 的解决方法

原因是 Github 被 GFW 屏蔽了。 Windows 系统&#xff0c;打开 C:\Windows\System32\drivers\etc&#xff0c;复制其中的 hosts 文件至桌面&#xff0c;用文本编辑器或者其他工具打开。 复制以下内容进去&#xff1a; 140.82.114.4 github.com 151.101.1.6 github.global.ss…

常见的排序算法过程和比较分析

比较分析 排序类别排序算法时间复杂度&#xff08;最好&#xff09;时间复杂度&#xff08;最坏&#xff09;时间复杂度&#xff08;平均&#xff09;辅助空间复杂度稳定性插入排序直接插入排序O(n)O(n)O(n)O(1)稳定插入排序折半插入排序O(n)O(n)O(n)O(1)稳定插入排序希尔排序…