0%

事务隔离级别

MySQL 8.4支持的事务隔离级别包括 READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE。

临时修改

-- 查看当前隔离级别的命令
select @@global.transaction_isolation,@@transaction_isolation;
-- 修改全局隔离级别为读提交
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 修改会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

永久生效

# my.cnf
transaction-isolation = READ-COMMITTED

三个工具

  • protoc
  • protoc-gen-go
  • protoc-gen-go-grpc

protoc

Gitehub下载页下载最新的 protoc ,window 环境下载 protoc-27.0-win64.zip,解压后将 bin/protoc.exe 移动到 $GOPATH/bin 目录下。

注意:$GOPATH/bin 需要配置在系统环境变量 PATH 中.

protoc-gen-go

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

protoc-gen-go-grpc

go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

三个工具安装好后,在 $GOPATH/bin 下有:protoc.exeprotoc-gen-go.exeprotoc-gen-go-grpc.exe 就算安装好了。

测试

准备一个 hello.proto :

syntax = "proto3";

option go_package = "./;proto";

package proto;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloResponse) {}
  rpc SayHi (HelloRequest) returns (HelloResponse) {}
}

message HelloRequest {
  string name = 1;
}

message HelloResponse {
  string message = 1;
}

生成 hello.pb.gohello_grpc.pb.go

protoc --go_out=. --go-grpc_out=. hello.proto

MySQL 8.4 版本

不同版本配置不同!

  • server 1
server-id=1
log-bin=mysql-bin
binlog-format=ROW
max_binlog_size=1024M
slow_query_log=1
binlog_expire_logs_seconds =864000
# 异步落盘
sync-binlog=0
replica_skip_errors=all
# 自增ID偏移量设置
auto-increment-increment=2
auto-increment-offset=1
# 半同步复制
rpl_semi_sync_source_enabled=1
rpl_semi_sync_replica_enabled=1
# 开启GTID
gtid_mode=ON
enforce-gtid-consistency=ON

mysql_native_password=ON
  • server 2
server-id=2
log-bin=mysql-bin
binlog-format=ROW
max_binlog_size=1024M
slow_query_log=1
binlog_expire_logs_seconds =864000
# 异步落盘
sync-binlog=0
replica_skip_errors=all
# 自增ID偏移量设置
auto-increment-increment=2
auto-increment-offset=2
# 半同步复制
rpl_semi_sync_source_enabled=1
rpl_semi_sync_replica_enabled=1
# 开启GTID
gtid_mode=ON
enforce-gtid-consistency=ON

mysql_native_password=ON

启动半同步复制

-- 要求 MySQL 服务器支持动态加载
show variables like '%have_dynamic_loading%';

INSTALL PLUGIN rpl_semi_sync_source SONAME 'semisync_source.so';
INSTALL PLUGIN rpl_semi_sync_replica SONAME 'semisync_replica.so';

-- 确认安装是否成功:
SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE '%semi%';

-- 查看半同步监控 
SHOW STATUS LIKE 'Rpl_semi_sync%';

创建复制用户

CREATE USER 'repl'@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';

这个用户直接配置复制会报错。Message: Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection. 原因是用户默认密码加密方式是 caching_sha2_password, 需要改成 mysql_native_password.

ALTER USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
FLUSH PRIVILEGES;

通过GTID启动复制(2台MySQL分别执行)

-- 注意修改IP、端口等信息
CHANGE REPLICATION SOURCE TO SOURCE_HOST = '172.16.20.153',SOURCE_PORT = 3306,SOURCE_USER = 'repl',SOURCE_PASSWORD = 'password',SOURCE_AUTO_POSITION = 1;

启动

START REPLICA;

查看复制状态

SHOW REPLICA STATUS\G

双主模式下的读写分离

  1. 创建一个普通用户
CREATE USER 'test'@'%' IDENTIFIED BY 'Test@123456';
GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'test'@'%';
FLUSH PRIVILEGES;
  1. 超级用户可以在另一台MySQL动态设置只读
    这里需要开发一个动态设置的读VIP所在MySQL只读的工具.
-- 普通用户只能读,超级用户依然可以写
set global read_only = 1;

-- 检查设置的状态
show global variables like '%read_only%';

额外说明:super_read_onlyread_only不一样,super_read_only是所有用户都不能写。

1. 打开官网MySQL安装文档

2. 根据Linux版本下载yum源

cat /etc/os-release
yum localinstall mysql84-community-release-el7-1.noarch.rpm

3. 查看可安装的MySQL包

yum repolist enabled | grep mysql.*-community

4. 安装MySQL

# EL8 systems 需要先禁止.
yum module disable mysql
yum install mysql-community-server

5. 启动MySQL

systemctl start mysqld

6. 登录MySQL

# 查看生成的随机密码
cat /var/log/mysqld.log |grep password

mysql -uroot -p

7. 初始化操作

-- 重置root密码
ALTER USER 'root'@'localhost' IDENTIFIED BY 'Test@123';
-- 设置root可以远程登录
update mysql.user set  host='%' WHERE user='root';
-- 刷新权限
FLUSH PRIVILEGES;

问题: Error: 13 (Permission denied)

vim /etc/selinux/config 
# 关闭后重启系统
SELINUX=disabled

Raw和Exec

GORM 中,db.Rawdb.Exec 是执行 原生 SQL 查询的两种不同方法,它们有不同的用途和返回值。

  • db.Raw 用于执行原生 SQL 查询并返回记录。通常用于执行 SELECT 查询,需要使用 Scan 方法将结果扫描到指定的结构体或变量中
  • db.Exec 用于执行不需要返回记录的原生 SQL 查询,通常用于 INSERTUPDATEDELETE 语句,或者执行 DDL(数据定义语言)语句,如创建表或修改表结构, 不需要 Scan,可以直接检查 result.RowsAffectedresult.Error.

Raw

var users []User
result := db.Raw("SELECT id, name, email FROM users").Scan(&users)
if result.Error != nil {
    fmt.Println(result.Error)
    return
}

for _, user := range users {
    fmt.Printf("ID: %d, Name: %s, Email: %s\n", user.ID, user.Name, user.Email)
}

Exec

result := db.Exec("INSERT INTO users (name, email) VALUES (?, ?)", "John Doe", "john@example.com")
if result.Error != nil {
    fmt.Println(result.Error)
    return
}

fmt.Printf("Rows affected: %d\n", result.RowsAffected)

sync.Once

专门用于确保某些初始化代码只被执行一次,不论该代码由多少个 goroutine 调用.

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var once sync.Once
    for i := 0; i < 10; i++ {
        go func() {
            // sync.Once 提供了一个方法 Do,这个方法接受一个函数作为参数,并确保该函数只会被执行一次
            once.Do(func() {
                fmt.Println("once")
            })
        }()
    }

    time.Sleep(1 * time.Second)
}

常用于单例模式,如

package main

import (
    "fmt"
    "sync"
)

type Singleton struct {
    // Fields for the singleton
}

var instance *Singleton
var once sync.Once

func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{}
        fmt.Println("Singleton instance created")
    })
    return instance
}

sync.WaitGroup

WaitGroup 用于等待一组 goroutine 完成。可以增加计数器,并在 goroutine 完成时减少计数器

package main

import (
    "fmt"
    "sync"
)

func main() {
    var once sync.Once
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            once.Do(func() {
                fmt.Println("once")
            })
        }()
    }

    wg.Wait()
}

sync.Mutex

Mutex 是一个互斥锁,用于保护临界区,确保同一时间只有一个 goroutine 能够访问被保护的代码.

package main

import (
    "fmt"
    "sync"
)

var count int
var mutex sync.Mutex

// 通过Mutex解决线程安全问题
func Increase() {
    mutex.Lock()
    count++
    mutex.Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 500; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            Increase()
        }()
    }
    wg.Wait()
    fmt.Println(count)
}

sync.RWMutex

RWMutex 是一种读写互斥锁,允许多个读操作同时进行,但写操作会独占锁。

package main

import (
    "fmt"
    "sync"
)

var counter int
var rwMutex sync.RWMutex

func read() int {
    rwMutex.RLock()
    defer rwMutex.RUnlock()
    return counter
}

func write(val int) {
    rwMutex.Lock()
    counter = val
    rwMutex.Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            write(i)
            fmt.Println("Read value:", read())
        }(i)
    }
    wg.Wait()
}