mysql中 redo日志(下)

大家好。上篇文章我们介绍了什么是redo日志以及redo日志的写入过程。建议没看过上篇文章的同学先看一下《mysql那些事儿》之 redo日志(上),今天我们继续来说一说redo日志。

一、redo日志文件

1. redo日志刷盘时机

我们知道mtr运行过程中产生的一组redo日志在mtr结束时会被复制到log buffer中,可是这些日志总在内存里呆着也不是个办法,在一些情况下它们会被刷新到磁盘里,比如:

  1. log buffer空间不足时:log buffer的大小是有限的(通过系统变量 innodb_log_buffer_size 指定),如果不停的往这个有限大小的log buffer 里塞入日志,很快它就会被填满。InnoDB认为如果当前写入log buffer 的redo日志量已经占满了log buffer总容量的大约一半左右,就需要把这些日志刷新到磁盘上。
  2. 事务提交时:我们前边说过之所以使用redo日志主要是因为它占用的空间少,还是顺序写,在事务提交时可以不把修改过的Buffer Pool页面刷新到磁盘,但是为了保证持久性,必须要把修改这些页面对应的redo日志刷新到磁盘。
  3. 后台线程不停的刷刷刷:后台有一个线程,大约每秒都会刷新一次log buffer中的redo日志到磁盘。
  4. 正常关闭服务器时。
  5. 做所谓的checkpoint时(一会儿介绍过checkpoint的概念)
  6. 其他的一些情况…

2. redo日志文件组

MySQL的数据目录(使用 SHOW VARIABLES LIKE ‘datadir’ 查看)下默认有两个名为ib_logfile0和ib_logfile1的文件, log buffer中的日志默认情况下就是刷新到这两个磁盘文件中。如果我们对默认的 redo 日志文件不满意,可以通过下边几个启动参数来调节:

innodb_log_group_home_dir

该参数指定了redo 日志文件所在的目录,默认值就是当前的数据目录。

innodb_log_file_size

该参数指定了每个redo日志文件的大小。

innodb_log_files_in_group

该参数指定redo日志文件的个数,默认值为2,最大值为100。

从上边的描述中可以看到,磁盘上的redo日志文件不只一个,而是以一个日志文件组的形式出现的。这些文件以ib_logfile[数字]的形式进行命名。在将redo日志写入日志文件组时,是 从ib_logfile0开始写,如果 ib_logfile0 写满了,写入ib_logfile1中,依此类推。如果写到最后一个文件,那就重新转到ib_logfile0继续写,所以整个过程如下图所示:
图片

总共的redo 日志文件大小其实就是:innodb_log_file_size × innodb_log_files_in_group。

3. redo日志文件格式

log buffer本质上是一片连续的内存空间,被划分成了若干个512字节大小的block。将log buffer中的redo日志刷新到磁盘的本质就是把block的镜像写入日志文件中,所以redo日志文件其实也是由若干个512 字节大小的block组成。

redo日志文件组中的每个文件大小都一样,格式也一样,都是由两部分组成:

  1. 前2048个字节,也就是前4个block是用来存储一些管理信息的。

  2. 从第2048字节往后是用来存储log buffer中的block镜像的。

所以我们前边所说的循环使用redo日志文件,其实是从每个日志文件的第2048个字节开始算,画个示意图就是这样:

图片

下面我们介绍一下每个redo日志文件前2048个字节,也就是前4个特殊block的格式都是干嘛的,废话少说,直接看图:

在这里插入图片描述

从图中可以看出来,这4个block分别是:

log file header: 描述该 redo 日志文件的一些整体属性,看一下它的结构:

图片

各个属性的具体释义如下:

属性名长度(字节)描述
LOG_HEADER_FORMAT4redo 日志的版本
LOG_HEADER_PAD14用于字节填充,没有什么意义。
LOG_HEADER_START_LSN8标记本 redo 日志文件开始的LSN值,也就是文件偏移量为2048字节初处对应的LSN值
LOG_HEADER_CREATOR32一个字符串, 标记本redo 日志文件的创建者是谁。正常运行时该值为MySQL 的版本号,比如:“MySQL 5.7.21” ,使用 mysqlbackup 命令创建的 redo 日志文件的该值为 “ibbackup” 和创建时间。
LOG_BLOCK_CHECKSUM4本block的校验值,所有block都有,我们不关心

checkpoint1: 记录关于 checkpoint 的一些属性,结构如下所示:
图片

属性长度(字节)描述
LOG_CHECKPOINT_NO8服务器做 checkpoint 的编号,每做 一次checkpoint ,该值就加1。
LOG_CHECKPOINT_LSN8服务器做 checkpoint 结束时对应的 LSN 值,系 统崩溃恢复时将从该值开始。
LOG_CHECKPOINT_OFFSET8上个属性中的 LSN 值在 redo 日志文件组中的 偏移量
LOG_CHECKPOINT_LOG_BUF_SIZE8服务器在做 checkpoint 操作时对应的 log buffer 的大小
LOG_BLOCK_CHECKSUM4本block的校验值,所有block都有,我们不关心

第三个block未使用,忽略~

checkpoint2: 结构和checkpoint1一样。

二、Log Sequeue Number

自mysql开始运行,就会不断的生成redo日志。所以redo日志的量在不断的递增,永远不会缩减。Inno为记录已经写入的redo日志量,设计了一个称之为Log Sequeue Number的全局变量–日志序列号,简称 lsn 。InnoDB规定初始的lsn值为8704。

我们知道在向log buffer中写入redo日志时不是一条一条写入的,而是以一个mtr生成的一组redo日志为单位进行写入的。而且实际上是把日志内容写在了log block body处。但是在统计lsn的增长量时,是按照实际 写入的日志量加上占用的log block header和log block trailer来计算的。我们来看一个例子:

系统第一次启动后初始化log buffer时, buf_free(就是标记下一条 redo日志应该写入到log buffer的位置的变量)就会指向第一个block的偏移量为12字节(log block header 的大小)的地方,那么 lsn 值也会跟着增加12:
图片

如果某个mtr产生的一组redo日志占用的存储空间比较小,也就是待插入的block剩余空闲空间能容纳这个mtr提交的日志时,lsn增长的量就是该mtr生成的redo日志占用的字节数,假设mtr_1产生的redo日志量为200字节,那么lsn就要在8716的基础上增加200 ,变为8916 。就像这样:
在这里插入图片描述

如果某个mtr产生的一组redo日志占用的存储空间比较大,也就是待插入的block剩余空闲空间不足以容纳这个mtr提交的日志时,lsn增长的量就是该mtr 生成的redo日志占用的字节数加上额外占用的log block header和log block trailer的字节数,就像这样:

在这里插入图片描述

所以:每一组由mtr生成的redo日志都有一个唯一的LSN值与其对应,LSN值越小,说明 redo日志产生的越早。

1. flushed_to_disk_lsn

redo日志是首先写到log buffer中,之后才会被刷新到磁盘上的redo 日志文件。所以InnoDB提出了一个称之为buf_next_to_write的全局变量,标记当前log buffer中已经有哪些日志被刷新到磁盘中了。画个图表示就是这样:
在这里插入图片描述

我们前边说lsn 是表示当前系统中写入的redo日志量,这包括了写到log buffer而没有刷新到磁盘的日志, 相应的,InnoDB提出了一个表示刷新到磁盘中的redo日志量的全局变量,称之为flushed_to_disk_lsn。
系统第一次启动时,该变量的值和初始的lsn值是相同的,都是 8704 。随着系统的运行,redo日志被不断写入log buffer,但是并不会立即刷新到磁盘, lsn的值就和flushed_to_disk_lsn的值拉开了差距。我们演示一下:
系统第一次启动后,向log buffer中写入了 mtr_1、mtr_2、mtr_3这三个mtr产生的redo日志,假设这三个mtr开始和结束时对应的lsn值分别是:

mtr_1:8716 ~ 8916。
mtr_2:8916 ~ 9948。
mtr_3:9948 ~ 10000。

此时的lsn 已经增长到了10000,但是由于没有刷新操作,所以此时flushed_to_disk_lsn的值仍为 8704 ,如图:在这里插入图片描述

随后进行将log buffer中的block刷新到redo日志文件的操作,假设将 mtr_1和mtr_2的日志刷新到磁盘,那么flushed_to_disk_lsn就应该增长 mtr_1和mtr_2写入的日志量,所以flushed_to_disk_lsn的值增长到了9948,如图:
在这里插入图片描述

综上所述,当有新的redo 日志写入到log buffer 时,首先lsn的值会增长,但flushed_to_disk_lsn不变, 随后随着不断有log buffer中的日志被刷新到磁盘上, flushed_to_disk_lsn的值也跟着增长。如果两者的值相同时,说明log buffer中的所有redo日志都已经刷新到磁盘中了。

2. lsn值和redo日志文件偏移量的对应关系

因为lsn的值是代表系统写入的redo日志量的一个总和,一个mtr中产生多少日志,lsn的值就增加多少,这样 mtr 产生的日志写到磁盘中时,很容 易计算某一个lsn值在redo日志文件组中的偏移量,如图:
图片

初始时的LSN值是8704,对应文件偏移量2048,之后每个mtr向磁盘中写入多少字节日志,lsn的值就增长多少。

3. lsn值和redo日志文件偏移量的对应关系

我们知道一个mtr代表一次对底层页面的原子访问,在访问过程中可能会产生一组不可分割的redo日志,在mtr结束时,会把这一组redo日志写入到log buffer中。除此之外,在mtr结束时还要把在mtr执行过程中可能修改过的页面加入到Buffer Pool的flush链表。我们看一下图:
在这里插入图片描述

当第一次修改某个缓存在Buffer Pool中的页面时,就会把这个页面对应的控制块插入到flush链表的头部,之后再修改该页面时由于它已经在flush链表中了,就不再次插入了。也就是说flush链表中的脏页是按照页面的第一次修改时间从大到小进行排序的。

在这个过程中会在缓存页对应的控制块中记录两个关于页面何时修改的属性:

oldest_modification: 如果某个页面被加载到Buffer Pool后进行第一次修改,那么就将修改该页面的mtr开始时对应的lsn值写入这个属性。

newest_modification: 每修改一次页面,都会将修改该页面的mtr 结束时对应的lsn值写入这个属性。也就是说该属性表示页面最近一次修改后对应的系统lsn值。

我们接着上讲的flushed_to_disk_lsn的例子看一下:

假设mtr_1执行过程中修改了页a ,那么在mtr_1执行结束时,就会将页a对应的控制块加入到flush链表的头部。并且将mtr_1开始时对应的lsn ,也就是8716 写入页a对应的控制块的oldest_modification属性中,把 mtr_1结束时对应的lsn,也就是8916写入页a对应的控制块的 newest_modification属性中。画个图表示一下(为了让图片美观一些,我们把 oldest_modification 缩写 成了o_m,把newest_modification 缩写成了 n_m):
图片

接着假设mtr_2执行过程中又修改了页b和页c两个页面,那么在mtr_2执行结束时,就会将页b和页c对应的控制块都加入到flush链表的头部。并且将mtr_2开始时对应的lsn,也就是8916写入页b和页c对应的控制块的oldest_modification属性中,把 mtr_2 结束时对应的lsn,也就是9948写入页b和页c对应的控制块的newest_modification属性中。画个图表示一下:

在这里插入图片描述

接着假设mtr_3执行过程中修改了页b和页d,不过页b之前已经被修改过了,所以它对应的控制块已经被插入到了flush链表,所以在mtr_3执行结束时,只需要将页d对应的控制块都加入到flush链表的头部即可。所以需要将mtr_3开始时对应的lsn,也就是9948写入页d对应的控制块的 oldest_modification属性中,把 mtr_3结束时对应的lsn,也就是10000写入页d对应的控制块的newest_modification属性中。另外,由于页b在 mtr_3 执行过程中又发生了一次修改,所以需要更新页 b对应的控制块中newest_modification的值为10000。画个图表示一下:
在这里插入图片描述

综上所述,flush链表中的脏页按照修改发生的时间顺序进行排序,也就是按照oldest_modification代表的LSN值进行排序,被多次更新的页面不会重复插入到flush链表中,但是会更新newest_modification属性的值。

三、checkpoint

我们要考虑一件事,就是redo日志文件组容量是有限的,我们不得不选择循环使用redo日志文件组中的文件,但是这会造成最后写的redo日志与最开始写的redo日志追尾,这时应该想到:redo日志只是为了系统崩溃后恢复脏页用的,如果对应的脏页已经刷新到了磁盘,也就是说即使现在系统崩溃,那么在重启后也用不着使用redo日志恢复该页面了,所以该redo日志也就没有存在的必要了,那么它占用的磁盘空间就可以被后续的 redo日志所重用。也就是说:判断某些redo日志占用的磁盘空间是否可以覆盖的依据就是它对应的脏页是否已经刷新到磁盘里。我们看一下前边一直说的那个例子:
图片
如图,虽然mtr_1和mtr_2生成的redo日志都已经被写到了磁盘上,但是它们修改的脏页仍然留在Buffer Pool中,所以它们生成的redo日志在磁盘上的空间是不可以被覆盖的。之后随着系统的运行,如果页a被刷新到了磁盘,那么它对应的控制块就会从flush链表中移除,就像这样子:
图片

这样mtr_1生成的redo日志就没有用了,它们占用的磁盘空间就可以被覆盖掉了。InnoDB提出了 一个全局变量checkpoint_lsn来代表当前系统中可以被覆盖的redo日志总量是多少,这个变量初始值也是8704 。比方说现在页a被刷新到了磁盘,mtr_1生成的redo日志就可以被覆盖了,所以我们可以进行一个增加checkpoint_lsn的操作,我们把这个过程称之为做一次 checkpoint 。做一次 checkpoint 其实可以分为两个步骤:

步骤一:计算一下当前系统中可以被覆盖的redo日志对应的lsn值最大是多少。redo 日志可以被覆盖,意味着它对应的脏页被刷到了磁盘,只要我们计算出当前系统中被最早修改的脏页对应的oldest_modification 值,那凡是在系统lsn值小于该节点的oldest_modification值时产生的redo日志都是可以被覆盖掉的,我们就把该脏页的oldest_modification 赋值给 checkpoint_lsn 。

比方说当前系统中页a已经被刷新到磁盘,那么flush链表的尾节点就是页c,该节点就是当前系统中最早修改的脏页了,它的oldest_modification值为8916,我们就把8916赋值给checkpoint_lsn。

步骤二:将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的信息之后, redo日志文件组中各个lsn值的关系就像这样:
在这里插入图片描述

四、 用户线程批量从flush链表中刷出脏页

一般情况下都是后台的线程在对LRU链表和flush链表进行刷脏操作, 这主要因为刷脏操作比较慢,不想影响用户线程处理请求。但是如果当前系统修改页面的操作十分频繁,这样就导致写日志操作十分频繁,系统lsn值增长过快。如果后台的刷脏操作不能将脏页刷出,那么系统无法及时做 checkpoint ,可能就需要用户线程同步的从flush链表中把那些最早修改的脏页( oldest_modification最小的脏页)刷新到磁盘,这样这些脏页对应的redo日志就没用了,然后就可以去做checkpoint了。

五、 查看系统中的各种LSN值

我们可以使用SHOW ENGINE INNODB STATUS命令查看当前 InnoDB存储引擎中的各种LSN值的情况,比如:

mysql> SHOW ENGINE INNODB STATUS\G 
(...省略前边的许多状态) 
LOG --- 
Log sequence number 124476971 
Log flushed up to   124099769 
Pages flushed up to 124052503 
Last checkpoint at  124052494 
0 pending log flushes, 0 pending chkp writes 
24 log i/o's done, 2.00 log i/o's/second ---------------------- 
(...省略后边的许多状态)

Log sequence number: 代表系统中的 lsn 值,也就是当前系统已经写入的redo日志量,包括写入log buffer中的日志。
Log flushed up to: 代表flushed_to_disk_lsn的值,也就是当前系统已经写入磁盘的redo日志量。
Pages flushed up to: 代表 flush链表中被最早修改的那个页面对应的oldest_modification属性值。
Last checkpoint at: 当前系统的checkpoint_lsn值。

六、 innodb_flush_log_at_trx_commit的用法

为了保证事务的持久性,用户线程在事务提交时需要将该事务执行过程中产生的所有redo日志都刷新到磁盘上。这一条要求太狠了,会很明显的降低数据库性能。如果对事务的持久性要求不是那么强烈的话,可以选择修改一个称为innodb_flush_log_at_trx_commit的系统变量的值,该变量有3个可选的值:

0 : 当该系统变量值为0时,表示在事务提交时不立即向磁盘中同步redo日志,这个任务是交给后台线程 做的。这样很明显会加快请求处理速度,但是如果事务提交后服务器挂了,后台线程没有及时将redo日志刷新到 磁盘,那么该事务对页面的修改会丢失。

1 : 当该系统变量值为1时,表示在事务提交时需要将redo日志同步到磁盘,可以保证事务的持久性。1也是innodb_flush_log_at_trx_commit 的默认值。

2 : 当该系统变量值为2时,表示在事务提交时需要将redo日志写到操作系统的缓冲区中,但并不需要保 证将日志真正的刷新到磁盘。这种情况下如果数据库挂了,操作系统没挂的话,事务的持久性还是可以保证的,但是操作系统也挂了的话,那就不能保证持久性了。

七、崩溃恢复

在服务器不挂的情况下,redo日志不仅没用,反而让性能变得更差。但是当数据库挂了,我们就可以在重启时根据redo日志中的记录就可以将页面恢复到系统崩溃前的状态。我们接下来大致看一下恢复过程。

1. 确定恢复的起点

checkpoint_lsn之前的redo日志都可以被覆盖,也就是说这些 redo 日志对应的脏页都已经被刷新到磁盘中了,既然它们已经被刷盘,我们就没必要恢复它们了。对于checkpoint_lsn之后的redo日志,它们对应的脏页可能没被刷盘,也可能被刷盘了,我们不能确定,所以需要从checkpoint_lsn开始读取redo日志来恢复页面。

当然,redo 日志文件组的第一个文件的管理信息中有两个block都存储了checkpoint_lsn的信息,我们当然是要选取最近发生的那次checkpoint的信息。衡量checkpoint发生时间早晚的信息就是所谓的 checkpoint_no, 我们只要把checkpoint1和checkpoint2这两个block中的 checkpoint_no值读出来比一下大小,哪个checkpoint_no值更大,说明哪个block存储的就是最近的一次checkpoint信息。这样我们就能拿到最近发生的checkpoint 对应的checkpoint_lsn值以及它在 redo 日志文件组中的偏移量checkpoint_offset。

2. 确定恢复的终点

redo日志恢复的起点确定了,那终点是哪个呢?这个还得从block的结构说起。我们说在写redo日志的时候都是顺序写的,写满了一个block之后会再往下一个block中写:
在这里插入图片描述
普通block的 log block header部分有一个称之为 LOG_BLOCK_HDR_DATA_LEN的属性,该属性值记录了当前block里使用了多少字节的空间。对于被填满的block来说,该值永远为512 。如果该属性的值不为512,那么它就是此次崩溃恢复中需要扫描的最后一个block。

3. 怎么恢复

确定了需要扫描哪些redo 日志进行崩溃恢复之后,接下来就是怎么进行恢复了。假设现在的redo日志文件中有5条redo日志,如图:
图片

由于redo 0在checkpoint_lsn后边,恢复时可以不管它。我们现在可以按照redo日志的顺序依次扫描checkpoint_lsn之后的各条redo日志,按照日志中记载的内容将对应的页面恢复出来。这样没什么问题,不过InnoDB还是想了一些办法加快这个恢复的过程:

使用哈希表: 根据redo日志的space ID和page number属性计算出散列值,把space ID和page number相同的redo日志放到哈希表的同一个槽里,如果有多个space ID和page number都相同的redo日志,那么它们之间使用链表连接起来,按照生成的先后顺序链接起来的,如图所示:在这里插入图片描述
之后就可以遍历哈希表,因为对同一个页面进行修改的redo日志都放在了一个槽里,所以可以一次性将一个页面修复好,这样可以加快恢复速度。

另外需要注意一点的是,同一个页面的redo日志是按照生成时间顺序进行排序的,所以恢复的时候也是按照这个顺序进行恢复,如果不按照生成时间顺序进行排序的话,那么可能出现错误。比如原先的修改操作是先插入一条记录,再删除该条记录,如果恢复时不按照这个顺序来,就可能变成先删除一条记录,再插入一条记录,这显然是错误的。

跳过已经刷新到磁盘的页面: checkpoint_lsn之前的redo日志对应的脏页确定都已经刷到磁盘了,但是 checkpoint_lsn之后的redo日志我们不能确定是否已经刷到磁盘,主要是因为在最近做的一次checkpoint后,可能后台线程又不断的从LRU链表和flush链表中将一些脏页刷出Buffer Pool 。这些 在checkpoint_lsn之后的redo日志,如果它们对应的脏页在崩溃发生时已经刷新到磁盘,那在恢复时也就没有必要根据redo 日志的内容修改该页面了。

那在恢复时怎么知道某个redo日志对应的脏页是否在崩溃发生时已经刷新到磁盘了呢?这还得从页面的结构说起,我们知道每个页面都有一个称之为File Header的部分,在File Header里有一个称之为 FIL_PAGE_LSN的属性,该属性记载了最近一次修改页面时对应的lsn值(其实就是页面控制块中的newest_modification值)。如果在做了某次 checkpoint之后有脏页被刷新到磁盘中,那么该页对应的FIL_PAGE_LSN 代表的lsn值肯定大于checkpoint_lsn的值,凡是符合这种情况的页面就不需要重复执行 lsn值小于 FIL_PAGE_LSN 的redo日志了,所以更进一步提升了崩溃恢复的速度。

八、 遗漏的问题:LOG_BLOCK_HDR_NO是如何计算的

我们前边说过,对于实际存储redo日志的普通的log block来说,在 log block header处有一个称之为LOG_BLOCK_HDR_NO的属性,我们说这个属性代表一个唯一的标号。这个属性是初次使用该block时分配的,跟当时的系统lsn值有关。使用下边的公式计算该block的 LOG_BLOCK_HDR_NO值:

((lsn / 512) & 0x3FFFFFFFUL) + 1

这个公式里的0x3FFFFFFFUL可能让大家有点困惑,其实它的二进制表示可能更亲切一点:
图片

从图中可以看出,0x3FFFFFFFUL对应的二进制数的前2位为0,后30位的值都为1 。一个二进制位与0做与运算(&)的结果肯定是0,一个二进制位与1做与运算(&)的结果就是原值。让一个数和0x3FFFFFFFUL 做与运算的意思就是要将该值的前2个比特位的值置为0,这样该值就肯定小于或等于 0x3FFFFFFFUL了。

这也就说明了,不论lsn多大, ((lsn / 512) & 0x3FFFFFFFUL) 的值肯定在0到0x3FFFFFFFUL之间,再加1的话肯定在1到0x40000000UL之间。而0x40000000UL这个值就代表着1GB。也就是说系统最多能产生不重复的LOG_BLOCK_HDR_NO值只有1GB个。InnoDB规定redo日志文件组中包含的所有文件大小总和不得超过512GB,一个block大小是512字节,也就是说redo日志文件组中包含的block块最多为1GB个,所以有1GB个不重复的编号值也就够用了。

另外,LOG_BLOCK_HDR_NO值的第一个比特位比较特殊,称之为 flush bit,如果该值为1,代表着本block是在某次将log buffer中的block刷新到磁盘的操作中的第一个被刷入的block。

好了,今天就讲到这里了,大家有什么想法欢迎留言讨论。也希望大家能给作者点个关注,谢谢大家!最后依旧是请各位老板有钱的捧个人场,没钱的也捧个人场,谢谢各位老板!

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

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

相关文章

这才是计科之 Onix XV6 源码分析(3、Unix-like系统的进程调度模块)

这才是计科之 Onix & XV6 源码分析(3、Unix-like系统的进程调度模块) 前言 前面已经分析了XV6的启动流程以及内存管理,接下来,我们探究进程调度的实现。与其说进程调度,我觉得可以顺应内存的虚拟化的叫法&#x…

禁用layui树形表格的多选框checkbox

1. 背景 在使用树形表格渲染数据时,需要对数据进行批量操作。相对于选中数据后,再做错误提示。直接把数据的多选框禁用掉更加直观。 2. 实现 DisabledTableCheckBox: () > {// 获取所有行 var tableElem $(".layui-table-fixed-l");var …

ALSA 用例配置

ALSA 用例配置。参考 ALSA 用例配置 来了解更详细信息。 ALSA 用例配置 用例配置文件使用 配置文件 语法来定义静态配置树。该树在运行时根据配置树中的条件和动态变量进行评估(修改)。使用 用例接口 API 解析结果并将其导出到应用程序。 配置目录和主…

【Git】如何不管本地文件,强制git pull

要在 Git 中强制执行 git pull 操作,忽略本地文件的更改,可以按照以下步骤操作: 保存当前工作状态:如果你有未提交的更改,可以使用 git stash 将这些更改存储起来。 git stash强制拉取最新代码:使用 git re…

java第二十一课 —— 快捷键,包,访问修饰符

IDEA 快捷键 删除行:Ctrl Y复制行:Ctrl D补全代码:Alt /添加取消注释:Ctrl /导入该行需要的类:Alt Enter快速格式化代码:Ctrl Shift L快速运行程序:Ctrl Shift F10生成构造器&#xf…

鸿蒙OS初识

学习官网:https://www.harmonyos.com/cn/develop 准备 注册,安装软件(node:12, DevEco Studio): https://developer.harmonyos.com/cn/docs/documentation/doc-guides/software_install-0000001053582415#ZH-CN_TOP…

【Rust】——面向对象设计模式的实现

🎼个人主页:【Y小夜】 😎作者简介:一位双非学校的大二学生,编程爱好者, 专注于基础和实战分享,欢迎私信咨询! 🎆入门专栏:🎇【MySQL&#xff0…

算法题--华为od机试考试(围棋的气、用连续自然数之和来表达整数、亲子游戏)

目录 围棋的气 题目描述 输入描述 示例1 输入 输出 解析 答案 用连续自然数之和来表达整数 题目描述 输入描述 输出描述 示例1 输入 输出 说明 示例2 输入 输出 解析 答案 亲子游戏 题目描述 输入描述 输出描述 示例1 输入 输出 说明 示例2 输入…

04 uboot 编译与调试

新手不需要详细掌握 uboot,只需要知道它是一个什么东西即可,工作中也只是改一些参数而已。 1、uboot 是什么 Linux 系统要启动就必须需要一个 bootloader 程序,也就说芯片上电以后先运行一段 bootloader 程序。这段 bootloader 程序会先初始化 DDR 等外设,然后将 Linux 内…

李飞飞解读创业方向:「空间智能」

在AI领域,李飞飞教授一直是一个举足轻重的存在。她的研究和见解不仅推动了计算机视觉的发展,更对人工智能的未来方向产生了深远的影响。在最近的一次演讲中,李飞飞详细解读了她对于「空间智能」的见解。本文将对她的演讲内容进行详细解读&…

正确挑选百兆超薄款工业级网络/脉冲变压器(网络隔离滤波器)

Hqst华强盛(石门盈盛电子)导读:工业级百兆超薄款网络变压器的生产要特殊的超薄磁芯配正确线径的铜线,使用符合相应防潮标准的凝固胶水。 一 ̖ 首先来看下商业级的超薄款的百兆网络变压器: 商业级(消费级&…

Hadoop+Spark大数据技术 实验11 Spark 图

17周期末考试 重点从第五章 scala语言开始 比如:映射(匿名函数) 11.3.1创建属性图 import org.apache.spark.graphx._ import org.apache.spark.rdd.RDD //创建一个顶点集的RDD val users: RDD[(VertexId ,(String,String))] sc.paralle…

每日题库:Huawe数通HCIA——全部【813道】

1.关于ARP报文的说法错误的是?单选 A.ARP报文不能被转发到其他广播域 B.ARP应答报文是单播方发送的 C.任何链路层协议都需要ARP协议辅助获取数据链路层标识 DARP请求报文是广播发送的 答案:C  解析: STP协议不需要ARP辅助 2.园区网络搭建时,使用以下哪种协议可以避免出现二层…

MATLAB设计ATF教程

打开Control System Designer 在MATLAB命令行窗口输入sisotool 出现如下Control System Designer窗口 基础Compensator 打开工具后,Compensator初始为1,需要按照需求进行设计。本示例的传递函数为: 基于上述传递函数的Bode图进行后续的设计…

C语言scanf( ) 函数、fprintf( ) 函数与 scanf( ) 函数和printf( ) 函数有什么不同?

一、问题 fscanf( ) 函数、fprintf( ) 函数与 printf( ) 函数、scanf( ) 函数的作⽤相似,都是格式化读写函 数,那么这两个读写函数有什么不同呢? 二、解答 两者的区别就在于前⾯的字符“f”,即 fscanfQ函数和 fprintfD函数的读写…

新买的移动硬盘无法识别

文章目录 背景解决方案 背景 同事新买的移动硬盘,插在电脑上识别不出来盘符,检查了一下,硬盘没问题应该,是ssk的硬盘盒M.2的SSD,硬盘驱动也是正常的,插拔了几次,都不识别,换了太电脑…

​在哪些场景下,使用SOCKS5代理会特别有用?(socks5代理ip)​

SOCKS5代理作为网络协议转换的利器,其独特功能在众多实际场景中展现出了极大的价值。以下是几个特定场景,其中SOCKS5代理的使用将变得尤为重要: 一、网络安全与隐私访问 1.高级渗透测试:在网络安全领域,渗透测试人员…

Android 高德地图API(新版)

新版高德地图 前言正文一、创建应用① 获取PackageName② 获取调试版安全码SHA1③ 获取发布版安全码SHA1 二、配置项目① 导入SDK② 配置AndroidManifest.xml 三、获取当前定位信息① ViewBinding使用和导包② 隐私合规设置③ 权限请求④ 初始化定位⑤ 获取定位信息 四、显示地…

postgresql之翻页优化

列表和翻页是所有应用系统里面必不可少的需求,但是当深度翻页的时候,越深越慢。下面是几种常用方式 准备工作 CREATE UNLOGGED TABLE data (id bigint GENERATED ALWAYS AS IDENTITY,value double precision NOT NULL,created timestamp with time zon…

Django中使用Celery和APScheduler实现定时任务

在之前的文章我们已经学习了Celery和APScheduler的基本使用,下面让我们来了解一下如何在Django中使用Celery和APScheduler Celery 1.前提工作 python 3.7 pip install celery pip install eventlet #5.0版本以下 pip install importlib-metadata4.8.3&#xff08…