【MySQL】MySQL 引擎特性 基于 GTID 复制实现的工作原理

GTID (global transaction IDentifier) 是全局事务标识。具有全局唯一性,一个 GTID 在一个服务器上只执行一次,从而避免重复执行导致数据混乱或主从不一致。

  1. GTID 怎么保证的全局唯一性?
    GTID = source_id(数据库uuid即server_uuid) : transaction_id(从1开始自增长的序列号)
  2. 什么是 server_uuid ?
    MySQL5.6 以后采用128位的 server_uuid 代替了32位的 server_id,server_id 依赖于my.cnf的手工配置,可能会有冲突,128位的uuid算法可以保证所有的 MySQL_uuid 不发生冲突。
  3. uuid算法怎么保证不冲突的?
    uuid=时间戳(当前时间戳,格林威治标准时间以来的纳秒数)+节点(机器Mac地址哈希)+时钟序列(随机生成两个字节)
  4. GTID 的优势
    在传统的复制里面,当发生故障需要主从切换时,服务器需要找到 binlog 和 pos 点,然后将其设为新的主节点开启复制,相对来说比较麻烦,也容易出错。在MySQL5.6里面,MySQL会通过内部机制自动匹配GTID断点,不再寻找 binlog 和 pos 点。
  5. MySQL 怎么自动匹配 GTID 断点?
  6. 在每个事务的开头,GTID 被获取并保存在内存中。事务完成后,GTID被记录到二进制日志中,并标记事务已提交。
  7. GTID EXCUTED 是从服务器中已经执行的 GTID 集合,会在内存和磁盘中同步保存,每次从服务器同步完成一个事务后,都会更新这些集合状态。
  8. 在服务器恢复时,MySQL 会读取 GTID 集合并恢复状态。通过对比 GTID 集合,确保恢复后的数据一致性

模拟GTID复制

go 复制代码
package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "strings"
    "sync"
    "time"
)

func initializeFile(filePath string, content string) {
    ioutil.WriteFile(filePath, []byte(content), 0644)
}

func appendToFile(filePath, content string) {
    f, _ := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0644)
    defer f.Close()
    f.WriteString(content)
}

func readFile(filePath string) string {
    content, _ := ioutil.ReadFile(filePath)
    return string(content)
}

func monitorSourceFileAndUpdateBinlog(sourceFile, binlogFile string, stopCh chan bool) {
    ticker := time.NewTicker(2 * time.Second)
    defer ticker.Stop()
    var lastContent string

    for {
        select {
        case <-stopCh:
            return
        case <-ticker.C:
            currentContent := readFile(sourceFile)
            if currentContent != lastContent {
                gtid := fmt.Sprintf("GTID=%d\n", time.Now().UnixNano())
                appendToFile(binlogFile, gtid)
                appendToFile(binlogFile, currentContent[len(lastContent):])
                lastContent = currentContent
            }
        }
    }
}

func monitorBinlogAndUpdateRelaylog(binlogFile, relaylogFile string, stopCh chan bool) {
    ticker := time.NewTicker(2 * time.Second)
    defer ticker.Stop()
    var lastContent string

    for {
        select {
        case <-stopCh:
            return
        case <-ticker.C:
            currentContent := readFile(binlogFile)
            if currentContent != lastContent {
                appendToFile(relaylogFile, currentContent[len(lastContent):])
                lastContent = currentContent
            }
        }
    }
}

func monitorRelaylogAndUpdateTarget(relaylogFile, targetFile string, stopCh chan bool) {
    ticker := time.NewTicker(2 * time.Second)
    defer ticker.Stop()
    var lastContent string

    for {
        select {
        case <-stopCh:
            return
        case <-ticker.C:
            currentContent := readFile(relaylogFile)
            if currentContent != lastContent {
                // Remove GTID lines
                lines := strings.Split(currentContent[len(lastContent):], "\n")
                for _, line := range lines {
                    if !strings.HasPrefix(line, "GTID=") && line != "" {
                        appendToFile(targetFile, line+"\n")
                    }
                }
                lastContent = currentContent
            }
        }
    }
}

func main() {
    var wg sync.WaitGroup
    stopCh := make(chan bool)

    // 文件路径与初始内容设置
    sourceFile := "txt1"
    targetFile := "txt2"
    binlogFile := "binlog"
    relaylogFile := "relaylog"

    // 初始化文件
    initializeFile(sourceFile, "Initial content\n")
    initializeFile(targetFile, "")
    initializeFile(binlogFile, "")
    initializeFile(relaylogFile, "")

    // 启动监控协程
    wg.Add(3)
    go func() {
        defer wg.Done()
        monitorSourceFileAndUpdateBinlog(sourceFile, binlogFile, stopCh)
    }()
    go func() {
        defer wg.Done()
        monitorBinlogAndUpdateRelaylog(binlogFile, relaylogFile, stopCh)
    }()
    go func() {
        defer wg.Done()
        monitorRelaylogAndUpdateTarget(relaylogFile, targetFile, stopCh)
    }()

    // 模拟用户输入以停止监控
    fmt.Println("Press Enter to stop monitoring...")
    fmt.Scanln()

    // 通知监控协程停止
    stopCh <- true

    // 等待所有协程结束
    wg.Wait()
    fmt.Println("Replication completed.")
}
相关推荐
问道飞鱼6 分钟前
【数据库知识】MySQL 多表关联高效实现指南:场景化方案与底层原理
数据库·mysql·多表关联
马克学长7 分钟前
SSM校园二手交易平台7fut7(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架·校园闲置资源交易
dblens 数据库管理和开发工具15 分钟前
MySQL :5.7与8.0版创建用户与授权、密码认证插件、角色、密码过期策略
数据库·mysql·dblens·mysql创建用户·mysql设置密码·mysql用户授权
曹牧26 分钟前
Oracle:字段为值列表
数据库·oracle
亮子AI33 分钟前
【Prisma】如何修复(重建)已经损坏的迁移历史?
数据库·prisma
卡尔特斯39 分钟前
Mysql ERROR 1524 (HY000): Plugin 'mysql_native_password' is not loaded
mysql
+VX:Fegn089544 分钟前
计算机毕业设计|基于springboot + vue职位管理推荐系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
热爱专研AI的学妹1 小时前
【搭建工作流教程】使用数眼智能 API 搭建 AI 智能体工作流教程(含可视化流程图)
大数据·数据库·人工智能·python·ai·语言模型·流程图
小韩博1 小时前
小迪第42课:PHP应用&MYSQL架构&SQL注入&跨库查询&文件读写&权限操作
sql·mysql·网络安全·架构·php
DBA小马哥1 小时前
国产数据库加速替代Oracle:聚焦信创背景下的平滑迁移与性能突破
数据库·oracle