日志:Redo Log 和 Undo Log

本篇文章主要介绍 Redo Log 和 Undo Log:

  1. 利用 Redo Log 和 Undo Log 实现本地事务的原子性、持久性
  2. Redo Log 的写回策略
  3. Redo Log Buffer 的刷盘时机

通过写入日志来保证原子性、持久性是业界的主流做法。

介绍 Redo Log 和 Undo Log

Redo Log 是什么:Redo Log 被称为重做日志。

Undo Log 是什么:Undo Log 被称为撤销日志、回滚日志。

技术是为了解决问题而生的,通过 Redo Log 我们可以实现崩溃恢复,防止数据更新丢失,保证事务的持久性。也就是说,在机器故障恢复后,系统仍然能够通过 Redo Log 中的信息,持久化已经提交的事务的操作结果。

技术是为了解决问题而生的,Undo Log 的作用 / 功能:

  • 事务回滚:可以对提前写入的数据变动进行擦除,实现事务回滚,保证事务的原子性。
  • 实现 MVCC 机制:Undo Log 也用于实现 MVCC 机制,存储记录的多个版本的 Undo Log,形成版本链。

Undo Log 中存储了回滚需要的数据。在事务回滚或者崩溃恢复时,根据 Undo Log 中的信息对提前写入的数据变动进行擦除。

Redo Log 和 Undo Log 都是用于实现事务的特性,并且都是在存储引擎层实现的。由于只有 InnoDB 存储引擎支持事务,因此只有使用 InnoDB 存储引擎的表才会使用 Redo Log 和 Undo Log。

实现本地事务的原子性、持久性

“Write-Ahead Log”日志方案

MySQL 的 InnoDB 存储引擎使用”Write-Ahead Log”日志方案实现本地事务的原子性、持久性。

“提前写入”(Write-Ahead),就是在事务提交之前,允许将变动数据写入磁盘。与”提前写入”相反的就是,在事务提交之前,不允许将变动数据写入磁盘,而是等到事务提交之后再写入。

“提前写入”的好处是:有利于利用空闲 I/O 资源。但”提前写入”同时也引入了新的问题:在事务提交之前就有部分变动数据被写入磁盘,那么如果事务要回滚,或者发生了崩溃,这些提前写入的变动数据就都成了错误。”Write-Ahead Log”日志方案给出的解决办法是:增加了一种被称为 Undo Log 的日志,用于进行事务回滚。

变动数据写入磁盘前,必须先记录 Undo Log,Undo Log 中存储了回滚需要的数据。在事务回滚或者崩溃恢复时,根据 Undo Log 中的信息对提前写入的数据变动进行擦除。

“Write-Ahead Log”在崩溃恢复时,会经历以下三个阶段:

  • 分析阶段(Analysis):该阶段从最后一次检查点(Checkpoint,可理解为在这个点之前所有应该持久化的变动都已安全落盘)开始扫描日志,找出所有没有 End Record 的事务,组成待恢复的事务集合(一般包括 Transaction Table 和 Dirty Page Table)。
  • 重做阶段(Redo):该阶段依据分析阶段中,产生的待恢复的事务集合来重演历史(Repeat History),找出所有包含 Commit Record 的日志,将它们写入磁盘,写入完成后增加一条 End Record,然后移除出待恢复事务集合。
  • 回滚阶段(Undo):该阶段处理经过分析、重做阶段后剩余的待恢复事务集合,此时剩下的都是需要回滚的事务(被称为 Loser),根据 Undo Log 中的信息回滚这些事务。

MySQL 中一条 SQL 更新语句的执行过程

以下的执行过程限定在,使用 InnoDB 存储引擎的表

  1. 事务开始
  2. 申请加锁:表锁、MDL 锁、行锁、索引区间锁(看情况加哪几种锁)
  3. 执行器找存储引擎取数据。
    4.

  4. 如果记录所在的数据页本来就在内存(innodb_buffer_cache)中,存储引擎就直接返回给执行器;

  5. 否则,存储引擎需要先将该数据页从磁盘读取到内存,然后再返回给执行器。
  6. 执行器拿到存储引擎给的行数据,进行更新操作后,再调用存储引擎接口写入这行新数据。
  7. 存储引擎将回滚需要的数据记录到 Undo Log,并将这个更新操作记录到 Redo Log,此时 Redo Log 处于 prepare 状态。并将这行新数据更新到内存(innodb_buffer_cache)中。同时,然后告知执行器执行完成了,随时可以提交事务。
  8. 手动事务 commit:执行器生成这个操作的 Binary Log,并把 Binary Log 写入磁盘。
  9. 执行器调用存储引擎的提交事务接口,存储引擎把刚刚写入的 Redo Log 改成 commit 状态。
  10. 事务结束

其中第 5 步,将这个更新操作记录到 Redo Log。生成的 Redo Log 是存储在 Redo Log Buffer 后就返回,还是必须写入磁盘后才能返回呢?
这就是 Redo Log 的写入策略,Redo Log 的写入策略由 innodb_flush_log_at_trx_commit 参数控制,该参数不同的值对应不同的写入策略。
还有第 6 步,把 Binary Log 写入磁盘和 Redo Log 一样,也有相应的写回策略,由参数 sync_binlog 控制。
通常我们说 MySQL 的”双 1″配置,指的就是 sync_binlog 和 innodb_flush_log_at_trx_commit 都设置成 1。也就是说,一个事务提交前,需要等待两次刷盘,一次是 Redo Log 刷盘(prepare 阶段),一次是 Binary Log 刷盘。

Redo Log 的两阶段提交 & 崩溃恢复

在上面【MySQL 中一条 SQL 更新语句的执行过程】部分,最后将 Redo Log 的写入拆成了两个步骤:prepare 和 commit,这就是”两阶段提交”。

“两阶段提交”的作用 / 目的:

  • 将事务设置为 prepare,为了在崩溃重启时,能够知道事务的状态
  • 保证两份日志(Binary Log 和 Redo Log)之间的逻辑一致。

如果先写 Redo Log,再写 Binary Log 或者 先写 Binary Log,再写 Redo Log,写入第一个日志后,如果此时发生了崩溃,那么第二个日志没有写入,就造成了两个日志的不一致。数据库的状态就有可能和用 Binary Log 恢复出来的库的状态不一致。备库利用 Binary Log 进行数据同步,就会出现主备库数据不一致的问题。具体的讲解可以看极客时间的专栏《MySQL实战45讲》

日志:Redo Log 和 Undo Log

而使用”两阶段提交”,遵守”崩溃恢复时,判断事务该提交、还是该回滚的规则”,就可以保证两份日志(Binary Log 和 Redo Log)之间的逻辑一致。

“崩溃恢复时,判断事务该提交、还是该回滚的规则”如下:

  1. 如果 Redo Log 里面的事务是完整的,也就是已经有了 commit 标识,那么利用该 Redo Log 中的信息,持久化事务的操作结果;
  2. 如果 Redo Log 里面的事务只有完整的 prepare,则判断对应事务的 Binary Log 是否存在并完整:
    a. 如果是,利用该 Redo Log 中的信息,持久化事务的操作结果;
    b. 如果否,则回滚事务,根据 Undo Log 中的信息对提前写入的数据变动进行擦除。

如果事务写入 Redo Log 处于 prepare 阶段之后、写 Binary Log 之前,发生了崩溃(也就是时刻 A 发生了崩溃),由于此时 Binary Log 还没写,Redo Log 也还没处于 commit 状态,所以崩溃恢复的时候,这个事务会回滚。这时 Binary Log 还没写,所以也不会传到备库。主库和备库的数据状态一致。

如果事务写入 Binary Log 之后,Redo Log 还没处于 commit 状态之前,发生了崩溃(也就是时刻 B 发生了崩溃),根据崩溃恢复时的判断规则中第 2 条,Redo Log 处于 prepare 阶段,Binary Log 完整,所以崩溃恢复的时候,会利用该 Redo Log 中的信息,持久化事务的操作结果。这时 Binary Log 已经写了,所以会传到备库。主库和备库的数据状态一致。

Binary Log 的写入在崩溃恢复时,判断事务该提交还是该回滚时,起到了至关重要的作用,只有 Binary Log 写入成功才能保证两份日志(Binary Log 和 Redo Log)之间的逻辑一致,才能考虑提交。

Redo Log 配置的选项

  • innodb_log_buffer_size:Redo Log Buffer 的内存大小
  • innodb_log_file_size:单个 Redo Log 文件的空间大小
  • innodb_log_files_in_group:Redo Log 文件的数量(默认值是 2)
  • innodb_log_group_home_dir:Redo Log 文件的存储目录(默认值是 .\ ,即数据目录)
  • innodb_flush_log_at_trx_commit:Redo Log 的写入策略

Redo Log 的写入策略

我们在【MySQL 中一条 SQL 更新语句的执行过程】部分的第 5 步中说:存储引擎将这行新数据更新到内存(innodb_buffer_cache)中。同时,将这个更新操作记录到 Redo Log,此时 Redo Log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。

生成的 Redo Log 是存储在 Redo Log Buffer 后就返回,还是必须写入磁盘后才能返回呢?这就是 Redo Log 的写入策略。Redo Log 的写入策略由 innodb_flush_log_at_trx_commit 参数控制。

我们可以通过修改该参数的值,设置 Redo Log 的写入策略,该参数可选的值有 3 个:

  • 值为 0 :表示每次事务提交时,只是把 Redo Log 存储到内存(Redo Log Buffer)就返回,不关心写入文件
  • 值为 1 :表示每次事务提交时,将 Redo Log Buffer 中的内容写入并同步到文件后才能返回(write + fsync 才能返回,这是参数的默认值)
  • 值为 2 :表示每次事务提交时,只是把 Redo Log Buffer 中的内容写入内核缓冲区,但不对文件进行同步,何时同步由操作系统来决定(write,fsync 的时机由操作系统决定)

Redo Log 文件组

MySQL 的数据目录(使用 show variables like ‘datadir’ 查看)下默认有两个名为 ib_logfile0 和

ib_logfile1 的文件,Redo Log Buffer 中的 Redo Log 默认情况下就是刷新到这两个磁盘文件中。

数据目录的位置也可以通过以下命令查看: select @@datadir;

日志:Redo Log 和 Undo Log
日志:Redo Log 和 Undo Log

如果我们对默认的 Redo Log 文件组不满意,可以通过下边几个启动参数来调节:

  • innodb_log_buffer_size:每个 Redo Log 文件的空间大小
  • innodb_log_file_size:每个 Redo Log 文件的最大空间大小
  • innodb_log_files_in_group:Redo Log 文件组中所有 Redo Log 文件的数量(默认值是 2,最大值是 100)
  • innodb_log_group_home_dir:Redo Log 文件的存储目录(默认值是 .\ ,即 MySQL 的数据目录)

从上边的描述中可以看到,磁盘上的 Redo Log 文件不只一个,而是以一个 日志文件组 的形式出现的。这些文件

以 ib_logfile[数字] ( 数字 可以是 0 、 1 、 2 …)的形式进行命名。

在将 Redo Log 写入 日志文件组 时,是从 ib_logfile0 开始写,如果 ib_logfile0 写满了,就接着 ib_logfile1 写,同理, ib_logfile1 写满了就去写 ib_logfile2 ,依此类推。

如果写到最后一个文件该怎么办呢?那就重新转到 ib_logfile0 继续写,所以整个过程如下图所示:

日志:Redo Log 和 Undo Log

总共的 Redo Log 文件空间大小其实就是:innodb_log_file_size × innodb_log_files_in_group 。(单个文件的空间大小 * 文件组中的文件个数)

如果采用循环使用的方式向 Redo Log 文件组里写数据的话,那就会造成追尾,也就是后写入的 Redo Log 覆盖掉前边写的 Redo Log。为了解决 Redo Log 的覆盖写入问题,InnoDB 的设计者提出了 checkpoint 的概念。

Redo Log 写入 Redo Log Buffer

Redo Log 的格式

InnoDB 的设计者为 Redo Log 定义了多种类型,以应对事务对数据库的不同修改场景,但是绝大部分类型的 Redo Log 都有下边这种通用的结构:

日志:Redo Log 和 Undo Log

各个部分的详细释义如下:

  • type:该条 Redo Log 的类型
  • space ID :表空间ID
  • page number :页号
  • data :该条 Redo Log 的具体内容

在 MySQL 5.7.21 这个版本中,InnoDB 的设计者一共为 Redo Log 设计了 53 种不同的类型。各种类型的 Redo Log 的不同之处在于 data 的具体结构不同。

Redo Log 的具体格式可以看掘金小册《MySQL 是怎样运行的:从根儿上理解 MySQL》

这些类型的 Redo Log 既包含 物理 层面的意思,也包含 逻辑 层面的意思,具体指:

  • 物理层面看,这些日志都指明了对哪个表空间的哪个页进行了什么修改。
  • 逻辑层面看,在系统奔溃重启时,并不能直接根据 Redo Log 中的信息,将页面内的某个偏移量处恢复成某个数据,而是需要调用一些事先准备好的函数,执行完这些函数后才可以将页面恢复成系统奔溃前的样子。

总结来说,Redo Log 中记录的是该操作对哪个表空间的哪个页的哪个偏移量进行了什么修改。

Mini-Transaction

一个事务可能包含多条 SQL 语句,每一条 SQL 语句可能包含多个「对底层页面的操作」,每个「对底层页面的操作」可能包含多个 Redo Log。这样的一个「对底层页面的操作」的过程被称为 Mini-Transaction,简称 mtr。

「对底层页面的操作」比如说:

  • 向聚簇索引对应的 B+ 树的某个页面中插入一条记录,插入一条记录这个操作可能包含多个 Redo Log

我们需要保证一个「对底层页面的操作」对应的多个 Redo Log 不可分割,即一个「对底层页面的操作」是原子的,这个操作对应的 Redo Log 要么都写入磁盘,要么都不写入磁盘。所以 InnoDB 的设计者规定在执行这些需要保证原子性的操作时必须以 组 的形式来记录 Redo Log,在进行奔溃恢复时,针对某个组中的 Redo Log,要么把全部的 Redo Log 都恢复掉,要么一个 Redo Log 也不恢复。

那么 InnoDB 的设计者是怎么做到分组的呢?InnoDB 的设计者在一个「对底层页面的操作」的最后一个 Redo Log 后面加上一个特殊类型的 Redo Log。相当于某个需要保证原子性的操作产生的一系列 Redo Log 必须要以一个特殊类型的 Redo Log 结尾,这样在奔溃恢复时:

  • 只有当解析到特殊类型的 Redo Log 时,才认为解析到了一组完整的 Redo Log,才会进行恢复。
  • 否则的话直接放弃前边解析到的 Redo Log。

日志:Redo Log 和 Undo Log

Redo Log Buffer

Redo Log Buffer 就是在服务器启动时,向操作系统申请的大一片连续的内存空间。

这片连续的内存空间被划分为若干个连续的用来存储 Redo Log 的数据页。

用来存储 Redo Log 的数据页被称为 Redo Log Block。

我们可以通过启动参数 innodb_log_buffer_size 来指定 Redo Log Buffer 的大小。

日志:Redo Log 和 Undo Log

日志:Redo Log 和 Undo Log
  • LOG_BLOCK_CHECKPOINT_NO :表示 checkpoint 的序号

日志:Redo Log 和 Undo Log

Redo Log 写入 Redo Log Buffer

我们前边说过一个 mtr 执行过程中可能产生若干个 Redo Log ,这些 Redo Log 是一个不可分割的组,所以其实并不是每生成一个 Redo Log,就将其插入到 Redo Log Buffer 中,而是每个 mtr 运行过程中产生的日志先暂时存到一个地方,当该 mtr 结束的时候,将过程中产生的一组 Redo Log 再全部复制到 Redo Log Buffer 中。

不同的事务可能是并发执行的,所以不同事务的 mtr 可能是交替执行的。每当一个 mtr 执行完成时,伴随

该 mtr 生成的一组 Redo Log 就需要被复制到 Redo Log Buffer 中,也就是说不同事务的 mtr 可能是交替写入 Redo Log Buffer 的。

Redo Log Buffer 中 Redo Log 的刷盘时机

mtr 运行过程中产生的一组 Redo Log 在 mtr 结束时会被复制到 Redo Log Buffer 中,在一些情况下 Redo Log Buffer 中的 Redo Log 会被写回磁盘,Redo Log 的刷盘时机如下:

  • Redo Log Buffer 的空间不足时,执行刷盘操作
  • 一个事务提交时,执行刷盘操作(需要设置指定参数)
  • 将某个脏页刷新到磁盘前,会先保证该脏页对应的 Redo Log 刷新到磁盘中(Redo Log 是顺序写入的,因此在将某个脏页对应的 Redo Log 从 Redo Log Buffer 刷新到磁盘时,也会保证将在其之前产生的 Redo Log 也刷新到磁盘中。
  • 后台线程不停的执行刷盘操作
  • 正常关闭服务器时,执行刷盘操作
  • 做 checkpoint 时,执行刷盘操作

Redo Log Buffer 的空间不足时,执行刷盘操作

Redo Log Buffer 的空间是有限的,空间大小由 innodb_log_buffer_size 来指定。

InnoDB 的设计者认为:如果 Redo Log Buffer 的内存被占用 1 / 2,就需要把 Redo Log Buffer 中的 Redo Log 刷新到磁盘。

一个事务提交时,执行刷盘操作

在前面【Redo Log 的写入策略】部分,讲到我们可以通过设置 innodb_flush_log_at_trx_commit 参数的值,在事务提交时执行刷盘操作后才能返回。

事务提交时执行刷盘操作后才能返回是 Redo Log 的默认写入策略。

后台线程不停的执行刷盘操作

后台有一个线程,每秒都会执行一次刷盘操作。后台线程执行刷盘操作的频率可以通过参数设置。

具体通过哪个参数设置,我也不清楚。

正常关闭服务器时,执行刷盘操作

做 checkpoint 时,执行刷盘操作

等等

Undo Log 的写回策略

MySQL中的 Undo Log 严格的讲不是 Log,而是数据,因此它的管理和落盘都跟数据是一样的:

  • Undo 的磁盘结构并不是顺序的,而是像数据一样按 Page 管理
  • Undo 写入时,也像数据一样产生对应的 Redo Log
  • Undo 的 Page 也像数据一样缓存在 Buffer Pool 中,跟数据 Page 一起做 LRU 换入换出,以及刷脏。Undo Page 的刷脏也像数据一样要等到对应的 Redo Log 落盘之后

之所以这样实现,首要的原因是 MySQL 中的 Undo Log 不只是承担 Crash Recovery 时保证 Atomic 的作用,更需要承担 MVCC 对历史版本的管理的作用,设计目标是高事务并发,方便的管理和维护。因此当做数据更合适。

但既然还叫 Log,就还是需要有 Undo Log 的责任,那就是保证 Crash Recovery 时,如果看到数据的修改,一定要能看到其对应 Undo 的修改,这样才有机会通过事务的回滚保证 Crash Atomic。标准的 Undo Log 这一步是靠 WAL 实现的,也就是要求 Undo 写入先于数据落盘。而 InnoDB 中 Undo Log 作为一种特殊的数据,这一步是通过 redo 的 min-transaction 保证的,简单的说就是数据的修改和对应的 Undo 修改,他们所对应的 Redo Log 被放到同一个 min-transaction 中,同一个 min-transaction 中的所有 Redo Log 在 Crash Recovery 时以一个整体进行重放,要么全部重放,要么全部丢弃。

作者:CatKang
链接:https://www.zhihu.com/question/267595935/answer/2204949497
来源:知乎

Undo Log 配置的选项

  • innodb_max_undo_log_size:值为,单个 Undo Log 最大可占用的字节存储空间;单位为,字节(默认值是 1 G)。
  • innodb_undo_directory:Undo Log 文件的存储目录(默认值是 .\ ,即数据目录)表示回滚日志的存储目录是数据目录,数据目录的位置可以通过查询变量”datadir”来查看。
  • innodb_undo_log_encrypt:Undo Log 是否加密(默认值是 off,即不加密)。
  • innodb_undo_log_truncate:Undo Log 是否自动截断回收(默认值是 on,即自动截断回收)。这个变量有效的前提是设置了使用独立表空间。
  • innodb_undo_tablespaces:值为 Undo Log 的独立表空间的数量(默认值是 0,即 Undo Log 没有独立的表空间,默认记录到共享表空间 ibdata 文件中)

日志:Redo Log 和 Undo Log

参考资料

20 | 日志(下):系统故障,如何恢复数据? (geekbang.org)

MySQL 是怎样运行的:从根儿上理解 MySQL – 小孩子4919 – 掘金课程 (juejin.cn)

02 | 日志系统:一条SQL更新语句是如何执行的?-极客时间 (geekbang.org)

关于Innodb undo log的刷新时机? – 知乎 (zhihu.com)

Original: https://www.cnblogs.com/feiyu2/p/16672433.html
Author: 真正的飞鱼
Title: 日志:Redo Log 和 Undo Log

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/598894/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 1_MySQL

    概念: 数据库是按照数据结构来组织, 存储和管理数据的仓库, 是一个长期存储在计算机内的, 有组织的, 有共享的, 统一管理的数据集合 分类: 网状结构数据库: 美国通用汽车公司I…

    数据库 2023年6月11日
    073
  • 关于ThreadLocal的一道面试题

    问:上面这段代码会输出什么?为什么? 为什么输出1然后空指针了? 输出1是没有任何问题的。那空指针是为什么呢? 因为这是两个线程,子线程和主线程。子线程设置1,主线程肯定拿不到啊。…

    数据库 2023年6月16日
    0106
  • Java学习-第一部分-第三阶段-第三节:MySQL基础

    零基础学MySQL 笔记目录:(https://www.cnblogs.com/wenjie2000/p/16378441.html) 一个问题 淘宝网,京东、微信,抖音都有各自的…

    数据库 2023年6月11日
    095
  • 【黄啊码】MySQL入门—2、使用数据定义语言(DDL)操作数据库

    大家好!我是黄啊码,上一节的知识点你了解多少了,掌握了多少了,别偷懒哦,今天我们就来掌握一下数据定义语言,说得高级点就是Data Definition Language,简称DDL…

    数据库 2023年6月16日
    097
  • 阿里慢SQL治理5大经典案例

    菜鸟供应链金融慢sql治理已经有一段时间,自己负责的应用持续很长时间没有慢sql告警,现阶段在推进组内其他成员治理应用慢sql。这里把治理过程中的一些实践拿出来分享下。 一、全表扫…

    数据库 2023年5月24日
    0127
  • 启用Hyper-v后,重启后界面提示 无法完成功能配置,正在撤销更改

    安装docker后,提示需要启用hyper-v,在控制面板中勾选Hyper-v,然后重启,更新快完成就提示无法完成功能配置,正在撤销更改 解决方法 方法1 控制面板一个一个选 方法…

    数据库 2023年6月14日
    0121
  • 当我用Python做了个自动工作汇报的脚本后,每天都闲的只能摸鱼

    哈喽兄弟们 之前经常编写Python脚本来进行数据处理、数据传输和模型训练。随着数据量和数据复杂性的增加,运行脚本可能需要一些时间。在等待数据处理完成时可以同时做一些其他工作。 为…

    数据库 2023年6月14日
    074
  • 时序数据库InfluxDB的基本语法

    一 了解InfluxDB的必要性 Time series data is a series of data points each associated with a specif…

    数据库 2023年6月16日
    097
  • 如何成为一名开发人员——第 3 部分:人际交往能力

    在前两节中,我介绍了技术和非技术技能。但是,编程生涯不能凭空出现!需要彼此才能茁壮成长。 你听说过”铁磨铁”这句话。这在软件开发行业当然是正确的。我的大部分…

    数据库 2023年6月14日
    079
  • 探究MySQL中SQL查询的成本

    成本 什么是成本,即SQL进行查询的花费的时间成本,包含IO成本和CPU成本。 IO成本:即将数据页从硬盘中读取到内存中的读取时间成本。通常1页就是1.0的成本。 CPU成本:即是…

    数据库 2023年5月24日
    0107
  • Mysql性能调优-工具篇

    首先祭出官方文档(这是5.7的,请自行选择版本): 如果你不想读英语,只需阅读这篇文章: [En] If you don’t want to read English,…

    数据库 2023年5月24日
    082
  • [Mysql]字符集相关

    下文为表述方便,混用”字符集”和”编码方案”这两个概念,不强调”Unicode字符集”。 系统变量 char…

    数据库 2023年6月16日
    085
  • 2_CSS

    1. 什么是CSS 1.1 什么是CSS Cascading Style Sheet 层叠样式表 是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一…

    数据库 2023年6月11日
    056
  • gitlab

    版本控制gitlab 1. 版本控制介绍 2. gitlab部署 版本控制介绍 版本控制是指对软件开发过程中各种程序代码、配置文件及说明文档等文件变更的管理,是软件配置管理的核心思…

    数据库 2023年6月14日
    098
  • MySQL知识点大全!!

    使用PreStatement对象: public int execUpdate(String sql, Object[] parms) { int count = 0; try {…

    数据库 2023年5月24日
    081
  • MySQL45讲之查询慢或者阻塞

    前言 本文介绍了表锁定和执行速度慢的实例,以及表锁定时的故障排除方法。 [En] This paper introduces examples of table locking a…

    数据库 2023年5月24日
    0112
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球