mysql 的undo log与redo log

问题1: mysql是磁盘数据库,那么他储存的数据都马上落盘了吗?

不是的,mysql 新增的数据都是先储存到缓冲区,再找合适的时机将缓冲区的数据一起落盘

问题 2: 什么时候数据落盘,为什么要这样做,有什么好处?

什么时候落盘不清楚,与缓冲区的大小与时间有关
好处: mysql 的写的性能会高非常多;就是磁盘顺序写入的性能是 60 多万的 qps,mysql 还不是顺序写,性能会更低, 但是内存写的性能在上千万qps,性能相差几十倍.

问题3: 那如果 mysql 突然宕机,或者突然断电怎么办.

那就是这里的主要内容

redo log

刚刚的问题 3:因为内存断电数据会丢失,那么mysql 是如何解决数据丢失的问题的呢?

mysql 采用的是WAL (Write-Ahead Logging)技术。
简单来讲就是采用记日志的方式避免数据的丢失,这里就是记录在 redo log 中;因为记录数据的开销比记录日志的开销更大

问题4: 为什么记录数据的开销更大,大多少?

数据的指定位置写是一件非常麻烦开销非常大的事情

比如同样 10 万条数据
顺序写

// 压力测试文件顺序写入
func TestFile(t *testing.T) {
	// 打开文件
	path := fmt.Sprintf("bench_file_%s.txt", time.Now().String())
	f, err := os.Create(path)
	if err != nil {
		t.Fatal(err)
	}
	// defer f.Close()
	start := time.Now()

	// 写入数据
	for i := 0; i < 10_0000; i++ {
		_, err := f.WriteString(fmt.Sprintf("data case %d", i))
		if err != nil {
			t.Fatal(err)
		}
	}
	// 时间 164.484541ms
	fmt.Println(time.Since(start))
}

指定位置(行)写

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"log"
	"os"
	"time"
)

// WriteToNthLine writes the string s to the nth line of the given file
func WriteToNthLine(file *os.File, n int, s string) error {
	if n < 1 {
		return fmt.Errorf("line number must be greater than 0")
	}

	// Read the file content into memory
	file.Seek(0, 0)
	scanner := bufio.NewScanner(file)
	var buffer bytes.Buffer

	currentLine := 1
	lineWritten := false

	for scanner.Scan() {
		if currentLine == n {
			// Write the new content to the buffer
			buffer.WriteString(s + "\n")
			lineWritten = true
		} else {
			// Write the original content to the buffer
			buffer.WriteString(scanner.Text() + "\n")
		}
		currentLine++
	}

	// If the target line is beyond the end of the file, append new lines
	for currentLine <= n {
		if currentLine == n {
			buffer.WriteString(s + "\n")
			lineWritten = true
		} else {
			buffer.WriteString("\n")
		}
		currentLine++
	}

	if !lineWritten {
		return fmt.Errorf("failed to write to the specified line")
	}

	// Write the buffer content back to the file
	file.Truncate(0)
	file.Seek(0, 0)
	_, err := file.Write(buffer.Bytes())
	if err != nil {
		return fmt.Errorf("failed to write to file: %w", err)
	}

	return nil
}

// 指定行写入

func main() {
	// 打开文件
	path := fmt.Sprintf("bench_file_%s.txt", time.Now().String())
	f, err := os.Create(path)
	if err != nil {
		log.Fatal(err)
	}
	// defer f.Close()
	start := time.Now()

	// 写入数据
	for i := 1; i < 10_0001; i++ {
		err := WriteToNthLine(f, i, fmt.Sprintf("data case %d", i))
		if err != nil {
			log.Fatal(err)
		}
	}
	// 时间2m34.389153084s
	fmt.Println(time.Since(start))
}

10 万条数据,顺序写需要 164.484541ms;而指定位置写需要2m34.389153084s;速度相差几百倍;

当然 mysql 不会使用这种暴力的写法;他每个数据的大小是固定的,这样就不会涉及到数据的重写问题,会快非常多,但是还是没有顺序写快

问题5 redo log会马上落盘吗?

不一定;首先不仅仅是写的数据会到 redo log;undo log 也会储存到 redo log;所以 redo log 也有一个缓冲区

redo log的自动落盘
  1. 每一秒

  2. 事务提交(innodb_flush_log_at_trx_commit=1)
    a. innodb_flush_log_at_trx_commit=0;提交落 redo log 的缓存
    b. innodb_flush_log_at_trx_commit=1提交落盘
    c. innodb_flush_log_at_trx_commit=2 提交落内核缓存

  3. 大于缓冲区的一半

  4. mysql 正常关闭

问题6 0 与 2 的区别?

0 : mysql故障(宕机),会丢数据
2 : mysql 故障,机器正常不会丢数据, 机器故障(突然断电)会丢数据
2 就是把数据交给操作系统了,操作系统正常数据就不会丢.

问题 7:事务进行到一半突然断电怎么办

mysql 的事务没有提交,就会回到事务开始前的状态,但是 redo log只能恢复到故障/断电之前,并不能恢复到事务之前的状态.

undo log

  • 作用:事务回滚,让数据回到事务开始之前的状态
  • 记录:undo log 记录更改前的数据版本 id 与状态
  • 如何回滚: 根据数据版本的 id,使用 undo log 的数据去覆盖当前的数据
问题 8 undo log 如何持久化?

跟其他数据的持久化方式相同,先储存缓冲区,在储存 redo log 的缓冲区,1s 落一次盘

问题 9 : 查询一条记录会进行缓存吗?

不是的,而是缓存一页数据(16k)

参考https://www.xiaolincoding.com/mysql/log/how_update.html#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81-redo-log

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值