重写redis组件

GO的优点

  • 直接编译为二进制。没有虚拟化损失

  • 自带运行环境、无需处理GC问题

  • 一次编码可以使用多种平台

  • 超强的并发支持和并发易用性

  • Go没有对象、没有类、没有继承

  • Go通过组合匿名字段来达到类似继承的效果

  • 去除了面向对象中复杂而冗余的部分

  • 保留了基本的面向对象的特性

使用Modules

内存对齐

runtime 的介绍

go的runtime被编译为用户程序的一部分,和程序一起运行

  • 内存管理

  • GC

  • 协程调度

  • 屏蔽一定的系统调用的差异性的能力

  • 一些go的关键字回转化为 runtime中的函数

    image-20240119210332409

image-20240119211356006

  • 词法分析:将代码分解为最小的语义化结构的片段

  • 句法分析:分解为语法树 SST

  • 语义分析:类型检查、类型推断、查看类型是否匹配、逃逸分析、函数调用内联

  • SSA代码分析

    • $env:GOSSAFUNC=”main”
    • go build
  • 查看plan9 汇编代码: go build -gcflags -S main.go

空结构体

  • 空结构体的地址均相同

  • 空结构体主要是为了节省内存

  • 用途: hashset、channel的类型

数据类型的长度

  • 使用 unsafe.Sizeof(uint(1)) 求数据的长度
  • int和指针的占用内存大小得看机器字长

nil、空接口和空结构体的区别

nil

  • nil 是空,但不一定是空指针
  • nil是六种类型的零值:pointer interface func channel map slice
  • 虽然为nil,但每种类型的nil是不同的,无法比较
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// interface pointer func chanel slice map can be nil
var a *int
fmt.Println(a == nil)

var b func()
fmt.Println(b == nil)

var c interface{}
fmt.Println(c == nil)

var d chan int
fmt.Println(d == nil)

var e map[int]string
fmt.Println(e == nil)

var f []int
fmt.Println(f == nil)
//true
//true
//true
//true
//true
//true

空接口

  • 两个属性都为 nil时,该interface才为nil接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var x interface{}
var y *string

fmt.Println(x == nil)
fmt.Println(y == nil)
x = y
// 完成赋值后,为nil的interface的type有了属性,此时该interface 不为 nil
fmt.Println(x == nil)
fmt.Println(y == nil)

//true
//true
//false
//true

空结构体

  • 空结构体是Go中的非常特殊的类型
  • 空结构体的值不是nil
  • 空结构体也不是nil,但都是相同的(zerobase)
1
2
3
4
5
6
var g struct{}
var h struct{}
fmt.Println(&g == &h)
fmt.Println(g == h)
//false
//true

总结:

  • nil是多个类型的零值,或者空值
  • 空结构体的指针和零值都不是nil
  • 空接口的零值是nil,一旦有了类型信息就不是nil

string

1
2
3
4
type stringStruct struct {
str unsafe.Pointer
len int
}

unicode

  • 一种统一的字符集
  • 其囊括了159种文字144679个字符
  • 14万个字符至少需要3个字节表示

UTF-8变长编码

  • Unicode的一种变长格式
  • 128个US-ASCII字符只需要一个字节编码
  • 西方常用字符需要两个字节
  • 其他字符需要三个字节,极少数需要四个字节

其中

  • 对字符串使用len得到的是字节数而不是字符数
  • 字符串直接用下标访问时得到的是字节
  • 字符串被range时,被解码成rune类型的字符

map

sync.Map的使用

协程

协程的抽象

image-20240123223754893

  • 协程的本质是一个g结构体
  • gobuf表示程序运行现场

线程的抽象

  • runtime中将操作系统线程抽象为m结构体
  • 其中g0是操作调度器
  • curg 表示目前线程运行的g

atomic操作

  • atomic是硬件层面加锁的机制
  • 只能用于简单变量的简单操作 cas + - load
  • 保证操作一个变量的时候,其他协程/线程无法访问

mutex

image-20240124113630509

其中 WaiterShift表示 等待锁的协程个数

正常模式 lock
  • 尝试使用CAS加锁

  • 无法直接获取,进行多次自旋尝试

  • 多次尝试失败,进入sema队列休眠

  • 正常模式下可能会有锁饥饿问题

饥饿模式
  • 当协程等待超过锁的时间超过了1ms,切换到饥饿模式
  • 饥饿模式中,不自旋,新来的协程直接进入sema休眠
  • 被唤醒的协程直接获取锁

锁竞争激烈时,互斥锁进入饥饿模式

·

socket连接

阻塞

image-20240124203722629

非阻塞IO

image-20240124203923073

多路复用epoll event-pool

image-20240124210714281

image-20240124210925706


重写redis组件
http://example.com/2024/01/19/重写redis组件/
作者
Forrest
发布于
2024年1月19日
许可协议