我们提供安全,免费的手游软件下载!
主从复制是数据库系统中常见的原理之一,其核心在于实现数据在主节点和从节点之间的同步。主节点负责接收写操作,从节点负责读取数据和备份。下面将介绍主从复制的步骤和网络模型。
主从复制的第一步是建立连接。从节点需要配置主节点的IP和端口,通过执行replicaof命令并发送psync命令来与主节点建立连接。
主节点会生成RDB文件并发送给从库,同时为每一个从节点开辟一块replication buffer缓冲区来记录从生成RDB文件开始收到的所有写命令。从库接收RDB文件后清空数据并加载RDB文件。
主节点会将replication buffer缓冲区中的数据(增量写命令)发送到从节点,从节点接收并执行,使从节点与主节点保持同步。
主从复制还维持着心跳机制,方便后续命令传输。网络模型主要包括阻塞IO、非阻塞IO、IO多路复用、信号驱动IO和异步IO。
IO多路复用中使用的文件描述符简称FD,可以提高IO效率。在IO多路复用中,有select、poll和epoll等多种方式。
select的流程是首先创建fd_set rfds,然后执行select指令,内核会遍历fd_set,最后遍历fd_set找到就绪的数据。然而,select存在一些问题,比如需要将整个fd_set从用户空间拷贝到内核空间,还需要遍历一次,并且最大监听数为1024。
poll将数据改为了链表,但仍需遍历。
epoll包括创建epoll实例、添加需要监听的fd以及等待fd就绪等步骤。epoll有两种通知模式:levelTriggered模式和EdgeTriggered模式。
在淘汰策略方面,有全体和ttl两种分类,其中LRU、抽样LRU和LFU是常见的淘汰策略。在RedisObject中使用逻辑访问次数来记录访问频率,并采取相应的淘汰策略。
文章总结了主从复制的原理和网络模型,以及IO多路复用的几种方式。同时提到了淘汰策略中的LRU、抽样LRU和LFU,对于理解数据库系统和网络模型具有一定的参考意义。
附带的TCP连接Redis客户端是使用Go语言编写的。
下面是客户端的Go语言代码。
package main
import (
"bufio"
"errors"
"fmt"
"io"
"net"
"strconv"
)
// RedisClient 封装用于连接
type RedisClient struct {
conn net.Conn
//包装一层 方便读写
writer *bufio.Writer
reader *bufio.Reader
}
func main() {
//连接redis
conn, err := net.Dial("tcp", "127.0.0.1:6379")
if err != nil {
fmt.Println("连接redis失败", err)
}
client := RedisClient{conn: conn, writer: bufio.NewWriter(conn), reader: bufio.NewReader(conn)}
defer client.conn.Close()
//发送命令
client.sendRequest([]string{"set", "name", "方块"})
//zrange boards:2024-4 0 -1
//client.sendRequest([]string{"zrange", "boards:2024-4", "0", "-1"})
//LRANGE tlist 0 -1
client.sendRequest([]string{"LRANGE", "tlist", "0", "-1"})
}
func (client *RedisClient) sendRequest(args []string) interface{} {
length := len(args)
firstCommand := fmt.Sprintf("%s%d", "*", length)
client.writeCommand(firstCommand)
for _, s := range args {
n := len(s)
client.writeCommand("$" + strconv.Itoa(n))
client.writeCommand(s)
println(n, s)
}
response := client.handleResponse()
return response
}
// 写命令
func (client *RedisClient) writeCommand(s string) {
client.conn.Write([]byte(s + "\r\n"))
}
// 解析返回结果
func (client *RedisClient) handleResponse() interface{} {
r, _, _ := client.reader.ReadRune()
flag := string(r)
switch flag {
case "+":
return client.ReadLine()
case "-":
return client.ReadLine()
case ":":
line := client.ReadLine()
res, _ := strconv.Atoi(line)
return res
case "$":
readRune := client.ReadLine()
length := string(readRune)
if length == "-1" {
return nil
} else if length == "0" {
return ""
}
lll, _ := strconv.Atoi(length)
bytes := make([]byte, lll+2)
n, _ := client.reader.Read(bytes)
return string(bytes[:n])
case "*":
return client.readBulkString()
default:
return errors.New("错误")
}
}
// 读一行
func (client *RedisClient) ReadLine() string {
bytes, _, _ := client.reader.ReadLine()
return string(bytes)
}
// 读到末尾
func (client *RedisClient) ReadToEnd() string {
var size = 1024
bytes := make([]byte, size)
var temp = ""
for {
n, err := client.reader.Read(bytes)
temp += string(bytes[:n])
if err == io.EOF || n == 0 || n < size {
break
}
}
return temp
}
func (client *RedisClient) readBulkString() interface{} {
counts, _ := strconv.Atoi(client.ReadLine())
if counts <= 0 {
return nil
}
var lists []interface{}
for i := 0; i < counts; i++ {
res := client.handleResponse()
lists = append(lists, res)
}
return lists
}
热门资讯