├── .gitignore ├── client ├── redisClient_test.go └── redisClient.go ├── go.mod ├── .cirrus.yml ├── adt ├── ziplist.go ├── dictEntry.go ├── listNode.go ├── stringObject.go ├── sds_test.go ├── set.go ├── redisObject_test.go ├── list.go ├── zskiplist_test.go ├── zskiplist.go ├── lru_test.go ├── dict.go ├── sds.go ├── redisObject.go └── dictHt.go ├── readme.md ├── tool ├── channelPool.go └── command.go ├── go.sum ├── main.go ├── server ├── redisServer.go ├── redisDb_test.go └── redisDb.go └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* -------------------------------------------------------------------------------- /client/redisClient_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module redis 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect 7 | github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70 // indirect 8 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /.cirrus.yml: -------------------------------------------------------------------------------- 1 | container: 2 | image: golang:latest 3 | 4 | env: 5 | GOPROXY: https://goproxy.cn 6 | 7 | test_task: 8 | modules_cache: 9 | #fingerprint_script: cat go.sum 10 | folder: $GOPATH/pkg/mod 11 | get_script: go get ./... 12 | build_script: go build ./main.go 13 | test_script: 14 | - go test ./adt 15 | - go test ./server 16 | - go test ./client -------------------------------------------------------------------------------- /adt/ziplist.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | type ZipList struct { 4 | zlBytes uint32 // 整个压缩列表占用的内存字节数 5 | zlTail uint32 // 压缩列表尾节点距离压缩列表起始地址有多少字节 6 | zlLen uint16 //压缩列表的节点数量 值等于 65535 时,需要遍历 7 | entryX int // 各个节点 8 | zlEnd uint8 //oxFF 压缩列表的末端 9 | } 10 | 11 | type EntryX struct { 12 | previousEntryLength int 13 | encoding int 14 | content []byte 15 | } 16 | -------------------------------------------------------------------------------- /adt/dictEntry.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | type DictEntry struct { 4 | next *DictEntry 5 | key *RedisObject 6 | v *RedisObject // c 里面是 int64 uint64, *v 这里不方便实现,直接使用了 redisObject 7 | } 8 | 9 | func NewDictEntry() *DictEntry { 10 | return &DictEntry{} 11 | } 12 | 13 | func (d *DictEntry) setKey(key *RedisObject) *DictEntry { 14 | d.key = key 15 | return d 16 | } 17 | 18 | func (d *DictEntry) setValue(value *RedisObject) *DictEntry { 19 | d.v = value 20 | return d 21 | } 22 | -------------------------------------------------------------------------------- /adt/listNode.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | type ListNode struct { 4 | Prev *ListNode 5 | Next *ListNode 6 | Value *RedisObject 7 | } 8 | 9 | func NewListNode() *ListNode { 10 | return &ListNode{} 11 | } 12 | 13 | func (l *ListNode) SetValue(value *RedisObject) *ListNode { 14 | l.Value = value 15 | return l 16 | } 17 | 18 | func (l *ListNode) SetPrev(prev *ListNode) *ListNode { 19 | l.Prev = prev 20 | return l 21 | } 22 | 23 | func (l *ListNode) SetNext(next *ListNode) *ListNode { 24 | l.Next = next 25 | return l 26 | } 27 | -------------------------------------------------------------------------------- /adt/stringObject.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | import "unsafe" 4 | 5 | type StringObject struct { 6 | int 7 | ele unsafe.Pointer 8 | } 9 | 10 | func NewStringObject() *StringObject { 11 | return &StringObject{} 12 | } 13 | 14 | func (obj *StringObject) SetInt(num int) *StringObject { 15 | obj.int = num 16 | return obj 17 | } 18 | 19 | func (obj *StringObject) GetInt() int { 20 | return obj.int 21 | } 22 | 23 | func (obj *StringObject) SetString(str *string) *StringObject { 24 | obj.ele = SdsNewLen([]byte(*str)) 25 | return obj 26 | } 27 | -------------------------------------------------------------------------------- /adt/sds_test.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestSds(t *testing.T) { 8 | 9 | newSds8Buf := SdsNewLen([]byte("hello world2, do you know youhello world2, do you know you ")) 10 | flagsPoint := GetFlagsPointByBufPoint(newSds8Buf) 11 | flagsValue := (*uint8)(flagsPoint) 12 | 13 | if *flagsValue != 3 { 14 | t.Error("flags value wrong") 15 | } 16 | } 17 | 18 | func BenchmarkSdshdr_Set(b *testing.B) { 19 | buf := []byte("hello world2, do you know youhello world2, do you know you ") 20 | for i := 0; i < b.N; i++ { 21 | SdsNewLen(buf) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /adt/set.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | import "reflect" 4 | 5 | type IntSet struct { 6 | encoding uint32 // 编码方式 7 | length uint32 //结合包含元素数量 8 | contents []byte // 保存元素的数组 从小到大排列,不含重复项 9 | } 10 | 11 | const ( 12 | INSERT_ENC_INIT16 = "int16" 13 | INSERT_ENC_INIT32 = "int32" 14 | INSERT_ENC_INIT64 = "int65" 15 | ) 16 | 17 | type EncodingType struct { 18 | } 19 | 20 | func (i *IntSet) Add(number interface{}) { 21 | numberType := reflect.TypeOf(number).String() 22 | if numberType == "int16" { 23 | 24 | } else if numberType == "int32" { 25 | 26 | } else if numberType == "int64" { 27 | 28 | } 29 | } 30 | 31 | type EncodingEnum struct { 32 | } 33 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # slog 2 | 3 | ![Cirrus CI - Base Branch Build Status](https://img.shields.io/cirrus/github/Dragonbuf/redis) 4 | ![Cirrus CI - Task and Script Build Status](https://img.shields.io/cirrus/github/Dragonbuf/redis?task=test) 5 | ## 介绍 6 | 之前使用的都是 redis3.2 的,现在准备根据 redis 5.0 重构这个项目 7 | 8 | # redis 基本数据结构实现 9 | - [ ] sdshdr(简动态字符串) 10 | - [ ] zskiplist (跳跃表) 11 | - [ ] list (链表) 12 | - [ ] dict (字典) 13 | - [ ] set(整数集合) 14 | - [ ] ziplist(压缩列表) 15 | - [ ] obj 对象 16 | 17 | 18 | 19 | # redis 数据结构基本 API 20 | - [ ] sdshdr 21 | - [x] SdsNewLen 22 | - [ ] SdsFree 23 | - [x] SdsReqType 24 | - [x] SdsHdrSize 25 | 26 | 27 | # 其他基本数据结构 28 | -------------------------------------------------------------------------------- /tool/channelPool.go: -------------------------------------------------------------------------------- 1 | package tool 2 | 3 | import "sync" 4 | 5 | type GoroutinePool struct { 6 | c chan struct{} 7 | wg *sync.WaitGroup 8 | } 9 | 10 | // 有缓冲 channel 11 | func NewGoroutinePool(maxSize int) *GoroutinePool { 12 | if maxSize <= 0 { 13 | panic("too small max size") 14 | } 15 | return &GoroutinePool{ 16 | make(chan struct{}, maxSize), 17 | new(sync.WaitGroup), 18 | } 19 | } 20 | 21 | func (g *GoroutinePool) Add(delta int) { 22 | g.wg.Add(delta) 23 | for i := 0; i < delta; i++ { 24 | g.c <- struct{}{} 25 | } 26 | } 27 | 28 | func (g *GoroutinePool) Done() { 29 | <-g.c 30 | g.wg.Done() 31 | } 32 | 33 | func (g *GoroutinePool) Wait() { 34 | g.wg.Wait() 35 | } 36 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= 2 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 3 | github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70 h1:XTnP8fJpa4Kvpw2qARB4KS9izqxPS0Sd92cDlY3uk+w= 4 | github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 5 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c= 6 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 7 | -------------------------------------------------------------------------------- /client/redisClient.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "redis/adt" 5 | "unsafe" 6 | ) 7 | 8 | const RedisReplyChunkBytes = 16 * 1024 9 | 10 | type RedisClient struct { 11 | fd int // 套接字描述符 12 | name *adt.RedisObject 13 | flags int // 客户端角色及所处的状态 14 | queryBuf unsafe.Pointer // 保存客户端发送的命令请求 超过 1GB 关闭此连接 15 | argc int 16 | argv []*adt.RedisObject // 命令参数 17 | cmd map[unsafe.Pointer]*RedisCommand 18 | buf [RedisReplyChunkBytes]byte // 固定缓存区 19 | bufPos int 20 | reply adt.List // 可变大小缓冲区 使用链表链接多个结构 21 | authenticated int // 是否通过身份验证 22 | } 23 | 24 | type RedisCommand struct { 25 | // 命令的实现函数 26 | // 命令应该给定的参数 27 | // 命令的总执行次数 28 | // 总消耗时长 29 | // 命令的标志 30 | } 31 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | _ "net/http/pprof" 6 | "redis/server" 7 | "redis/tool" 8 | "strconv" 9 | ) 10 | 11 | var CurrentDb int 12 | 13 | func main() { 14 | fmt.Println("[暂时只支持 get set hset hget del hdel expire exit 命令]\n ") 15 | for { 16 | fmt.Printf("go-redis 【" + strconv.Itoa(server.Server.GetCurrentDbNumber()) + "】-> ") 17 | args := &tool.GoRedisArgs{} 18 | if _, err := fmt.Scanln(&args.Command, &args.Key, &args.Fields, &args.Value); err != nil { 19 | // 这里有问题,但是暂时忽略 20 | } 21 | DoCommand(args) 22 | } 23 | 24 | } 25 | 26 | func DoCommand(args *tool.GoRedisArgs) { 27 | 28 | if server.Db.ExpireIfNeeded(args.Command) { 29 | server.Db.Del(args.Command) 30 | server.Db.DelExpire(args.Command) 31 | } 32 | 33 | clientCmd := tool.GetSupportCommand(args) 34 | 35 | if cmd, ok := clientCmd[args.Command]; ok { 36 | 37 | if cmd.ArgsNumber != args.Size() { 38 | fmt.Println("命令需要 ", cmd.ArgsNumber, " 个参数,但是仅提供了 ", args.Size()) 39 | return 40 | } 41 | 42 | // todo 多个 list 时,需要 incr 多次 43 | server.Server.IncrDirty() 44 | cmd.Function(cmd.Args) 45 | } else { 46 | fmt.Println("未找到此命令 :" + args.Command) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /adt/redisObject_test.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestNewRedisObject(t *testing.T) { 9 | 10 | obj := NewRedisObject().SetTypes(RedisString).SetEncoding(RedisEncodingInt).SetPtr(&Object{}) 11 | 12 | if obj.types != RedisString { 13 | t.Error("wrong types") 14 | } 15 | 16 | if obj.encoding != RedisEncodingInt { 17 | t.Error("wrong encoding") 18 | } 19 | 20 | obj.Set(4) 21 | 22 | if obj.types != RedisString { 23 | t.Error("wrong types") 24 | } 25 | 26 | if obj.encoding != RedisEncodingInt { 27 | t.Error("wrong encoding") 28 | } 29 | 30 | if obj.int != 4 { 31 | t.Error("wrong int") 32 | } 33 | 34 | str := "you" 35 | obj.Set(&str) 36 | 37 | if reflect.TypeOf(obj.Get()).String() != "unsafe.Pointer" { 38 | t.Error("wrong type", reflect.TypeOf(obj.Get()).String()) 39 | } 40 | 41 | if obj.types != RedisString { 42 | t.Error("wrong types") 43 | } 44 | 45 | if string(*(*[]byte)(obj.Ele)) != "you" { 46 | t.Error("wrong int ", string(*(*[]byte)(obj.Ele))) 47 | } 48 | } 49 | 50 | func BenchmarkRedisObject_Set(b *testing.B) { 51 | 52 | obj := NewRedisObject() 53 | for i := 0; i < b.N; i++ { 54 | obj.Set(i) 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /server/redisServer.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "redis/adt" 5 | "unsafe" 6 | ) 7 | 8 | var Server *RedisServer 9 | var Db *RedisDb 10 | 11 | func init() { 12 | Server = NewRedisServer() 13 | Db = Server.Select(0) 14 | } 15 | 16 | type RedisServer struct { 17 | dbNumber int 18 | currentDb int 19 | redisDb [16]*RedisDb // 这里写死,只有 16 个数据库 20 | saveParams 21 | dirty int64 // 修改计数器 22 | timeT int64 // 上一次执行保存时间 23 | aofBuffer unsafe.Pointer // AOF 缓冲区 24 | clients adt.List // 保存所有客户端状态 25 | statPeakMemory int64 // 已使用内存峰值 26 | } 27 | 28 | type saveParams struct { 29 | timeT int // 秒数 30 | changes int // 修改数 31 | } 32 | 33 | func NewRedisServer() *RedisServer { 34 | var redisDb [16]*RedisDb 35 | for i := 0; i < 16; i++ { 36 | redisDb[i] = NewRedisDb() 37 | } 38 | 39 | server := &RedisServer{} 40 | server.dbNumber = 16 41 | server.redisDb = redisDb 42 | return server 43 | } 44 | 45 | func (c *RedisServer) Select(db int) *RedisDb { 46 | Db = c.redisDb[db] 47 | c.currentDb = db 48 | return c.redisDb[db] 49 | } 50 | 51 | func (c *RedisServer) BGSave() { 52 | ch := make(chan int, 1) 53 | <-ch // 后台处理保存 54 | } 55 | func (c *RedisServer) Save() { 56 | // 立即保存 57 | } 58 | 59 | func (c *RedisServer) GetDbTotal() int { 60 | return c.dbNumber 61 | } 62 | 63 | func (c *RedisServer) GetCurrentDbNumber() int { 64 | return c.currentDb 65 | } 66 | 67 | func (c *RedisServer) IncrDirty() { 68 | c.dirty++ 69 | } 70 | -------------------------------------------------------------------------------- /adt/list.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | type List struct { 4 | head *ListNode 5 | tail *ListNode 6 | len uint64 7 | } 8 | 9 | func NewList() *List { 10 | return &List{} 11 | } 12 | 13 | func (l *List) SetHead(head *ListNode) *List { 14 | l.head = head 15 | return l 16 | } 17 | func (l *List) SetTail(tail *ListNode) *List { 18 | l.tail = tail 19 | return l 20 | } 21 | 22 | func (l *List) LPush(value *RedisObject) int { 23 | 24 | l.IncrLen() 25 | 26 | if l.HasOneNode() { 27 | node := NewListNode().SetValue(value) 28 | l.SetHead(node).SetTail(node) 29 | } else { 30 | node := NewListNode().SetNext(l.head).SetValue(value) 31 | l.SetHead(node) 32 | } 33 | 34 | return 1 35 | } 36 | 37 | func (l *List) LPop() *RedisObject { 38 | node := l.head 39 | if node == nil { 40 | return nil 41 | } 42 | 43 | l.SetHead(node.Next) 44 | l.decrLen() 45 | 46 | if l.IsEmpty() { 47 | l.SetTail(nil) 48 | } 49 | 50 | return node.Value 51 | } 52 | 53 | func (l *List) RPush(value *RedisObject) int { 54 | 55 | l.IncrLen() 56 | if l.HasOneNode() { 57 | node := NewListNode().SetValue(value) 58 | l.SetHead(node).SetTail(node) 59 | } else { 60 | node := NewListNode().SetValue(value).SetPrev(l.tail) 61 | l.SetTail(node) 62 | } 63 | 64 | return 1 65 | } 66 | 67 | func (l *List) RPop() *RedisObject { 68 | node := l.tail 69 | if node == nil { 70 | return nil 71 | } 72 | 73 | l.SetTail(node.Prev) 74 | l.decrLen() 75 | 76 | return node.Value 77 | } 78 | 79 | func (l *List) IncrLen() { 80 | l.len++ 81 | } 82 | func (l *List) decrLen() { 83 | l.len-- 84 | } 85 | 86 | func (l *List) HasOneNode() bool { 87 | return l.len == 1 88 | } 89 | 90 | func (l *List) IsEmpty() bool { 91 | return l.len == 0 92 | } 93 | -------------------------------------------------------------------------------- /adt/zskiplist_test.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | type Server struct { 10 | Addr string 11 | timeout time.Duration 12 | tls string 13 | } 14 | 15 | type config struct { 16 | tls string 17 | } 18 | 19 | type ZSkip struct { 20 | Head string 21 | Tail string 22 | Level int 23 | } 24 | 25 | func NewZSip(options ...func(skip *ZSkip)) *ZSkip { 26 | zs := &ZSkip{} 27 | 28 | for _, option := range options { 29 | option(zs) 30 | } 31 | 32 | return zs 33 | } 34 | 35 | func CreateHead(name string) func(*ZSkip) { 36 | return func(skip *ZSkip) { 37 | skip.Head = name 38 | } 39 | } 40 | 41 | func CreateLevel(l int) func(*ZSkip) { 42 | return func(skip *ZSkip) { 43 | skip.Level = l 44 | } 45 | } 46 | 47 | func NewServer(addr string, options ...func(*Server)) (*Server, error) { 48 | srv := &Server{ 49 | Addr: addr, 50 | } 51 | 52 | for _, option := range options { 53 | option(srv) 54 | } 55 | 56 | return srv, nil 57 | } 58 | 59 | func timeout(d time.Duration) func(*Server) { 60 | return func(srv *Server) { 61 | srv.timeout = d 62 | } 63 | } 64 | 65 | func tls(c *config) func(*Server) { 66 | return func(srv *Server) { 67 | Tls := loadConfig(c) 68 | srv.tls = Tls 69 | } 70 | } 71 | 72 | func loadConfig(c *config) string { 73 | return c.tls 74 | } 75 | 76 | //使用 77 | func BenchmarkZSkipList_Create(b *testing.B) { 78 | src, err := NewServer("localhost:8080", timeout(1), tls(&config{tls: "12"})) 79 | fmt.Println(src, err) 80 | 81 | sk := NewZSip(CreateHead("head"), CreateLevel(666)) 82 | fmt.Println(sk.Level) 83 | } 84 | 85 | func BenchmarkZslRandomLevel(b *testing.B) { 86 | for i := 0; i < b.N; i++ { 87 | ZslRandomLevel() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /adt/zskiplist.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | import ( 4 | "math/rand" 5 | "unsafe" 6 | ) 7 | 8 | const ZSkipListP = 0.25 9 | 10 | var ZSkipListMaxLevel = 64 11 | 12 | func ZslRandomLevel() int { 13 | level := 1 14 | for float64(rand.Int()&0XFFFF) < (ZSkipListP * 0XFFFF) { 15 | level += 1 16 | } 17 | 18 | if level < ZSkipListMaxLevel { 19 | return level 20 | } 21 | return ZSkipListMaxLevel 22 | } 23 | 24 | // Redis 只在:实现有序集合键、在集群节点中用作内部数据结构 中使用了跳跃表 25 | type ZSkipList struct { 26 | Header, Tail *ZSkipListNode // 指向跳跃表的头节点 尾节点 27 | Length uint64 // 跳跃表的长度: 跳跃表目前包含节点的数量 (不算表头 ) 28 | Level int32 // 记录跳跃表内 层数最大节点的层数 (不算表头) 29 | } 30 | 31 | type ZSkipListNode struct { 32 | Ele unsafe.Pointer // 这里其实是 sds 33 | source float64 34 | BackWard *ZSkipListNode // 后退指针 35 | Level []ZSkipListLevel // 根据 power law(越大的数,出现的概率越小 ) 随机生成一个 1-32 直接的值作为 level 的大小 36 | } 37 | 38 | type ZSkipListLevel struct { 39 | Forward *ZSkipListNode // 前进指针 40 | span uint32 //跨度 41 | } 42 | 43 | func NewZSkipListNode() *ZSkipListNode { 44 | return &ZSkipListNode{ 45 | Ele: nil, 46 | source: 0, 47 | BackWard: nil, 48 | Level: nil, 49 | } 50 | } 51 | 52 | func NewZSkipList() *ZSkipList { 53 | 54 | header := NewZSkipListNode() 55 | level := ZslRandomLevel() 56 | zsl := &ZSkipList{ 57 | Header: header, 58 | Tail: nil, 59 | Length: 0, 60 | Level: 1, 61 | } 62 | 63 | for j := 0; j < level; j++ { 64 | zsl.Header.Level[j].Forward = nil 65 | zsl.Header.Level[j].span = 0 66 | } 67 | 68 | header.BackWard = nil 69 | 70 | return zsl 71 | } 72 | 73 | // source 31 level 3 74 | // create node 75 | func (zsl *ZSkipList) Create() { 76 | 77 | } 78 | 79 | func (zsl *ZSkipList) Insert() { 80 | //查找要插入的位置 81 | // 调整跳跃表的高度 82 | //插入节点 83 | //调账 backward 84 | } 85 | 86 | // 找到插入的位置 87 | func (zsl *ZSkipList) Find() { 88 | for i := zsl.Level - 1; i > 0; i-- { 89 | var rank [64]int32 90 | if i == zsl.Level-1 { 91 | rank[i] = i 92 | } else { 93 | rank[i] = rank[i+1] 94 | } 95 | } 96 | } 97 | 98 | func (zsl *ZSkipList) ZRange(start, end int, withScores bool) { 99 | 100 | } 101 | 102 | func (zsl *ZSkipList) ZCard(start, end int, withScores bool) { 103 | 104 | } 105 | -------------------------------------------------------------------------------- /server/redisDb_test.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestNewRedisDb(t *testing.T) { 8 | db := NewRedisDb() 9 | key := "fuck" 10 | key2 := "c" 11 | value := "you" 12 | 13 | if db.Get(key2) != "" { 14 | t.Error("nil key found value") 15 | } 16 | 17 | db.Set(key, value) 18 | if db.Get(key) != value { 19 | t.Error("value not found") 20 | } 21 | } 22 | 23 | func TestHashSet(t *testing.T) { 24 | db := NewRedisDb() 25 | key := "fuck" 26 | filed := "c" 27 | value := "you" 28 | value1 := "notyouasc" 29 | 30 | filed2 := "c2" 31 | value2 := "you2" 32 | 33 | filed3 := "c3" 34 | 35 | err := db.HSet(key, filed, value) 36 | if err != nil { 37 | t.Error(err) 38 | } 39 | 40 | err = db.HSet(key, filed2, value2) 41 | if err != nil { 42 | t.Error(err) 43 | } 44 | 45 | if db.HGet(key, filed) != value { 46 | t.Error("not equal value " + value) 47 | } 48 | 49 | if db.HGet(key, filed2) != value2 { 50 | t.Error("not equal value " + value2) 51 | } 52 | 53 | if db.HGet(key, filed3) != "" { 54 | t.Error("not equal filed3 " + filed3) 55 | } 56 | 57 | err = db.HSet(key, filed, value1) 58 | if err != nil { 59 | t.Error(err) 60 | } 61 | 62 | if db.HGet(key, filed) == value { 63 | t.Error("not equal value " + db.HGet(key, filed)) 64 | } 65 | if db.HGet(key, filed) != value1 { 66 | t.Error("not equal value1 " + value1) 67 | } 68 | } 69 | 70 | func TestRedisDb_Del(t *testing.T) { 71 | db := NewRedisDb() 72 | db.Set("a", "b") 73 | db.Del("a") 74 | 75 | if db.Get("a") != "" { 76 | t.Error("can not get del key") 77 | } 78 | } 79 | 80 | func TestRedisDb_Hdel(t *testing.T) { 81 | db := NewRedisDb() 82 | _ = db.HSet("a", "1", "1") 83 | _ = db.HSet("a", "2", "2") 84 | db.Hdel("a", "1") 85 | 86 | if db.HGet("a", "1") != "" { 87 | t.Error("can not get hdel key") 88 | } 89 | } 90 | 91 | func TestRedisDb_RPush(t *testing.T) { 92 | 93 | db := NewRedisDb() 94 | 95 | listName := "newList" 96 | a := "a" 97 | b := "b" 98 | 99 | db.RPush(listName, a, b) 100 | if db.RPop(listName) != b { 101 | t.Error("pop not b") 102 | } 103 | 104 | if db.RPop(listName) != a { 105 | t.Error("pop not a") 106 | } 107 | 108 | if db.RPop(listName) != "" { 109 | t.Error("pop not ") 110 | } 111 | } 112 | 113 | func TestRedisDb_Expire(t *testing.T) { 114 | db := NewRedisDb() 115 | key := "fuck" 116 | filed := "c" 117 | value := "you" 118 | 119 | err := db.HSet(key, filed, value) 120 | if err != nil { 121 | t.Error(err) 122 | } 123 | 124 | db.Expire(key, "2") 125 | } 126 | -------------------------------------------------------------------------------- /adt/lru_test.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | import ( 4 | "container/list" 5 | "net/http" 6 | "strconv" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | var S *Store 12 | 13 | type Store struct { 14 | Mutex *sync.Mutex 15 | store map[string]*list.Element 16 | ll *list.List 17 | max int 18 | } 19 | 20 | // 直接返回 []byte 当 write 时,节省转换的时间 21 | func (s *Store) Get(key string) ([]byte, bool) { 22 | current, exist := s.store[key] 23 | if exist { 24 | expire := int64(current.Value.(*Node).expire) 25 | if expire == 0 || expire > time.Now().Unix() { 26 | s.ll.MoveToFront(current) 27 | return current.Value.(*Node).value, true 28 | } 29 | } 30 | return nil, false 31 | } 32 | 33 | func (s *Store) Set(key string, value []byte, expire int) { 34 | current, exist := s.store[key] 35 | if !exist { 36 | s.store[key] = s.ll.PushFront(&Node{ 37 | key: key, 38 | value: value, 39 | expire: expire, 40 | }) 41 | 42 | if s.max != 0 && s.ll.Len() > s.max { 43 | s.Delete(s.ll.Remove(s.ll.Back()).(*Node).key) 44 | } 45 | return 46 | } 47 | 48 | current.Value.(*Node).value = value 49 | current.Value.(*Node).expire = expire 50 | s.ll.MoveToFront(current) 51 | } 52 | 53 | func (s *Store) Delete(key string) { 54 | delete(s.store, key) 55 | } 56 | 57 | func (s *Store) Flush() { 58 | s.store = make(map[string]*list.Element) 59 | s.ll = list.New() 60 | } 61 | 62 | type Node struct { 63 | key string 64 | value []byte 65 | expire int 66 | } 67 | 68 | func Handler(f func(http.ResponseWriter, *http.Request)) func(w http.ResponseWriter, r *http.Request) { 69 | return func(w http.ResponseWriter, r *http.Request) { 70 | S.Mutex.Lock() 71 | defer S.Mutex.Unlock() 72 | f(w, r) 73 | } 74 | } 75 | 76 | func Get(w http.ResponseWriter, r *http.Request) { 77 | value, ok := S.Get(r.URL.Query().Get("key")) 78 | if !ok { 79 | _, _ = w.Write([]byte("no such key")) 80 | return 81 | } 82 | 83 | w.Header().Set("content-type", "text/plain") 84 | _, _ = w.Write(value) 85 | } 86 | 87 | func Set(w http.ResponseWriter, r *http.Request) { 88 | expire, _ := strconv.Atoi(r.URL.Query().Get("expire")) 89 | S.Set(r.URL.Query().Get("key"), []byte(r.URL.Query().Get("value")), expire) 90 | value, ok := S.Get(r.URL.Query().Get("key")) 91 | 92 | if !ok { 93 | _, _ = w.Write([]byte("no such key")) 94 | return 95 | } 96 | 97 | w.Header().Set("content-type", "text/plain") 98 | _, _ = w.Write(value) 99 | } 100 | 101 | func main() { 102 | S = &Store{ 103 | Mutex: &sync.Mutex{}, 104 | store: make(map[string]*list.Element), 105 | ll: list.New(), 106 | max: 100, 107 | } 108 | http.HandleFunc("/get", Handler(Get)) 109 | http.HandleFunc("/set", Handler(Set)) 110 | err := http.ListenAndServe(":9999", nil) 111 | if err != nil { 112 | panic(err) 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /tool/command.go: -------------------------------------------------------------------------------- 1 | // go-redis 所有支持的命令都在这里 2 | package tool 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | "redis/server" 8 | "strconv" 9 | ) 10 | 11 | type GoRedisArgs struct { 12 | Command string 13 | Key string 14 | Fields string 15 | Value string 16 | } 17 | 18 | type cmd struct { 19 | Function func(*GoRedisArgs) 20 | Args *GoRedisArgs 21 | ArgsNumber int 22 | } 23 | 24 | func GetSupportCommand(args *GoRedisArgs) map[string]cmd { 25 | return map[string]cmd{ 26 | "set": { 27 | func(args *GoRedisArgs) { 28 | if err := server.Db.Set(args.Key, args.Fields); err != nil { 29 | fmt.Println(err) 30 | } 31 | }, 32 | args, 33 | 3, 34 | }, 35 | "get": { 36 | func(args *GoRedisArgs) { 37 | fmt.Println(server.Db.Get(args.Key)) 38 | }, 39 | args, 40 | 2, 41 | }, 42 | "expire": { 43 | func(args *GoRedisArgs) { 44 | server.Db.Expire(args.Key, args.Fields) 45 | }, 46 | args, 47 | 3, 48 | }, 49 | "hdel": { 50 | func(args *GoRedisArgs) { 51 | fmt.Println(server.Db.Hdel(args.Key, args.Fields)) 52 | }, 53 | args, 54 | 3, 55 | }, 56 | "del": { 57 | func(args *GoRedisArgs) { 58 | fmt.Println(server.Db.Del(args.Key)) 59 | }, 60 | args, 61 | 2, 62 | }, 63 | "hset": { 64 | func(args *GoRedisArgs) { 65 | if err := server.Db.HSet(args.Key, args.Fields, args.Value); err != nil { 66 | fmt.Println(err) 67 | } 68 | }, 69 | args, 70 | 4, 71 | }, 72 | "hget": { 73 | func(args *GoRedisArgs) { 74 | fmt.Println(server.Db.HGet(args.Key, args.Fields)) 75 | }, 76 | args, 77 | 3, 78 | }, 79 | "rpush": { 80 | func(args *GoRedisArgs) { 81 | if args.Size() == 0 { 82 | server.Db.RPush(args.Key, args.Fields) 83 | } else { 84 | server.Db.RPush(args.Key, args.Fields, args.Value) 85 | } 86 | }, 87 | args, 88 | 4, 89 | }, 90 | "rpop": { 91 | func(args *GoRedisArgs) { 92 | fmt.Println(server.Db.RPop(args.Key)) 93 | }, 94 | args, 95 | 2, 96 | }, 97 | "select": { 98 | func(args *GoRedisArgs) { 99 | // todo 实现全局函数 100 | dbNum, _ := strconv.Atoi(args.Key) 101 | if dbNum >= server.Server.GetDbTotal() { 102 | fmt.Println("dbNum 不能超过 ", server.Server.GetDbTotal()) 103 | return 104 | } 105 | server.Server.Select(dbNum) 106 | return 107 | }, 108 | args, 109 | 2, 110 | }, 111 | "exit": { 112 | func(args *GoRedisArgs) { 113 | fmt.Println("good bye") 114 | os.Exit(1) 115 | }, 116 | args, 117 | 1, 118 | }, 119 | } 120 | } 121 | 122 | //所有非空的参数 123 | func (g *GoRedisArgs) Size() int { 124 | if g.Command == "" { 125 | return 0 126 | } 127 | 128 | if g.Key == "" { 129 | return 1 130 | } 131 | 132 | if g.Fields == "" { 133 | return 2 134 | } 135 | 136 | if g.Value == "" { 137 | return 3 138 | } 139 | 140 | return 4 141 | } 142 | -------------------------------------------------------------------------------- /adt/dict.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | // 类型特定函数 4 | type dicType string 5 | 6 | // 私有数据 暂时不实现 7 | type privateData struct{} 8 | 9 | type Dict struct { 10 | types dicType 11 | privateData privateData 12 | ht [2]DictHt 13 | treHashIdx int64 14 | } 15 | 16 | func NewDict() *Dict { 17 | return &Dict{ 18 | treHashIdx: -1, 19 | ht: [2]DictHt{ 20 | *NewDictHt().InitHtBySize(2), 21 | *NewDictHt(), 22 | }} 23 | } 24 | 25 | // key 暂时只支持 string 吧 26 | func (d *Dict) Hset(key *RedisObject, value *RedisObject) *Dict { 27 | 28 | // 正在 rehash 插入只插入 ht[1],其他情况只插入 ht[0] 29 | dictHt := d.GetWriteHt() 30 | dictHt.AddDictValue(key, value) 31 | 32 | // 插入完如果需要 rehash 则开始 rehash 33 | if dictHt.ShouldReHash() { 34 | d.BeginReHash() 35 | } 36 | 37 | return d 38 | } 39 | 40 | func (d *Dict) Hget(key *RedisObject) *RedisObject { 41 | 42 | dictHt := &d.ht[0] 43 | value := dictHt.findValue(key) 44 | 45 | if value != nil { 46 | return value 47 | } 48 | 49 | // 如果正在 rehash 需要分别查询 ht0 ht1 是否存在 50 | if !d.IsReHashing() { 51 | return nil 52 | } 53 | 54 | dictHt = &d.ht[1] 55 | return dictHt.findValue(key) 56 | } 57 | 58 | func (d *Dict) Hdel(key *RedisObject) int { 59 | 60 | dictHt := &d.ht[0] 61 | status := dictHt.delValue(key) 62 | 63 | // 如果正在 rehash 需要分别查询 ht0 ht1 是否存在 64 | if status == 0 && d.IsReHashing() { 65 | dictHt = &d.ht[1] 66 | status = dictHt.delValue(key) 67 | } 68 | 69 | return status 70 | } 71 | 72 | func (d *Dict) FinishedAllReHash() { 73 | d.ResetTreHashIdx() 74 | d.SwapHt() 75 | d.DestroyHt1() 76 | } 77 | 78 | func (d *Dict) BeginReHash() { 79 | if !d.IsReHashing() { 80 | d.treHashIdx++ 81 | } 82 | 83 | writeHt := d.GetWriteHt() 84 | readHt := d.GetReadHt() 85 | 86 | // 如果 ht[1] 没有申请空间 87 | if writeHt.IsEmpty() { 88 | writeHt.InitHtBySize(readHt.size * 2) 89 | } 90 | 91 | i := readHt.MoveTableToNewByIndex(d.treHashIdx, writeHt) 92 | d.FinishedCurrentIndexReHash(i) 93 | if readHt.FinishedReHash(d.treHashIdx) { 94 | d.FinishedAllReHash() 95 | } 96 | } 97 | 98 | func (d *Dict) IsReHashing() bool { 99 | return d.treHashIdx != -1 100 | } 101 | func (d *Dict) ResetTreHashIdx() { 102 | d.treHashIdx = -1 103 | } 104 | func (d *Dict) FinishedCurrentIndexReHash(i int) { 105 | d.treHashIdx += int64(i) 106 | } 107 | 108 | func (d *Dict) SwapHt() { 109 | d.ht[0] = d.ht[1] 110 | } 111 | 112 | func (d *Dict) DestroyHt1() { 113 | d.ht[1] = *NewDictHt() 114 | d.ht[1].used = 0 115 | d.ht[1].size = 0 116 | d.ht[1].sizeMask = 0 117 | } 118 | 119 | func (d *Dict) GetWriteHt() *DictHt { 120 | if d.IsReHashing() { 121 | return &d.ht[1] 122 | } 123 | 124 | return &d.ht[0] 125 | } 126 | 127 | func (d *Dict) GetReadHt() *DictHt { 128 | return &d.ht[0] 129 | } 130 | 131 | // 复制键函数 132 | func (d *Dict) CopyKey() {} 133 | 134 | // 复制值函数 135 | func (d *Dict) CopyValue() {} 136 | 137 | // 对比键函数 138 | func (d *Dict) Compare() {} 139 | 140 | // 销毁键函数 141 | func (d *Dict) DestroyKey() {} 142 | 143 | // 销毁键函数 144 | func (d *Dict) DestroyValue() {} 145 | -------------------------------------------------------------------------------- /adt/sds.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | const ( 8 | SdsType5 = iota 9 | SdsType8 10 | SdsType16 11 | SdsType32 12 | SdsType64 13 | ) 14 | 15 | type SdsHdr interface { 16 | SdsNewLen(len int) 17 | } 18 | 19 | type Sdshdr5 struct { 20 | flag uint8 // 低 3 位存类型,高 5 位存长度 21 | buf []byte 22 | } 23 | type Sdshdr8 struct { 24 | len uint8 // 已使用长度 25 | alloc uint8 //总长度 26 | flag uint8 //低 3 位存类型,高 5 位预留 27 | buf []byte 28 | } 29 | 30 | // 本来需要根据 buf 长度来返回不同的类型,但是现在 go 不支持 1字节对齐,所以只返回一种 31 | func SdsNewLen(s []byte) unsafe.Pointer { 32 | 33 | strLen := len(s) 34 | types := SdsReqType(strLen) 35 | if types == SdsType5 || strLen < 1<<32 { 36 | types = SdsType32 37 | } 38 | 39 | // 1 2 4 8 字节, 小于 1 字节暂不计算 40 | // 计算不同头部所需的空间 41 | //hdrlen := SdsHdrSize(types) 42 | 43 | switch types { 44 | case SdsType5: 45 | case SdsType8: 46 | sds := Sdshdr8{ 47 | len: uint8(strLen), 48 | alloc: uint8(strLen), 49 | flag: uint8(1), 50 | buf: s, 51 | } 52 | sdsPointer := unsafe.Pointer(&sds) 53 | return unsafe.Pointer(uintptr(sdsPointer) + unsafe.Offsetof(sds.buf)) 54 | case SdsType16: 55 | sds := Sdshdr16{ 56 | len: uint16(strLen), 57 | alloc: uint16(strLen), 58 | flag: uint8(2), 59 | buf: s, 60 | } 61 | sdsPointer := unsafe.Pointer(&sds) 62 | return unsafe.Pointer(uintptr(sdsPointer) + unsafe.Offsetof(sds.buf)) 63 | case SdsType32: 64 | sds := Sdshdr32{ 65 | len: uint32(strLen), 66 | alloc: uint32(strLen), 67 | flag: uint8(3), 68 | buf: s, 69 | } 70 | sdsPointer := unsafe.Pointer(&sds) 71 | return unsafe.Pointer(uintptr(sdsPointer) + unsafe.Offsetof(sds.buf)) 72 | } 73 | 74 | sds := Sdshdr64{ 75 | len: uint64(strLen), 76 | alloc: uint64(strLen), 77 | flag: uint8(4), 78 | buf: s, 79 | } 80 | sdsPointer := unsafe.Pointer(&sds) 81 | return unsafe.Pointer(uintptr(sdsPointer) + unsafe.Offsetof(sds.buf)) 82 | } 83 | 84 | func SdsFree() { 85 | 86 | } 87 | 88 | func SdsSetLen() { 89 | 90 | } 91 | 92 | func SdsHdrSize(types int) int { 93 | switch types { 94 | case SdsType5: 95 | return int(unsafe.Sizeof(Sdshdr5{})) 96 | case SdsType8: 97 | return int(unsafe.Sizeof(Sdshdr8{})) 98 | case SdsType16: 99 | return int(unsafe.Sizeof(Sdshdr16{})) 100 | case SdsType32: 101 | return int(unsafe.Sizeof(Sdshdr32{})) 102 | case SdsType64: 103 | return int(unsafe.Sizeof(Sdshdr64{})) 104 | } 105 | return 0 106 | } 107 | 108 | func SdsReqType(stringSize int) int { 109 | if stringSize < 1<<5 { 110 | return SdsType5 111 | } 112 | 113 | if stringSize < 1<<8 { 114 | return SdsType8 115 | } 116 | 117 | if stringSize < 1<<16 { 118 | return SdsType16 119 | } 120 | 121 | if stringSize < 1<<32 { 122 | return SdsType32 123 | } 124 | 125 | // 不考虑 32 位机器 126 | return SdsType64 127 | } 128 | 129 | // 根据 buf 长度计算 flag 太复杂,这里不在计算 130 | func PointOffset(types int) int { 131 | return 0 132 | } 133 | 134 | // 因为确定的 sdstype32 所以 -8 找到 flags 135 | func GetFlagsPointByBufPoint(buf unsafe.Pointer) unsafe.Pointer { 136 | return unsafe.Pointer(uintptr(buf) - uintptr(8)) 137 | } 138 | 139 | type Sdshdr16 struct { 140 | len uint16 // 已使用长度 141 | alloc uint16 //总长度 142 | flag uint8 //低 3 位存类型,高 5 位预留 143 | buf []byte 144 | } 145 | type Sdshdr32 struct { 146 | len uint32 // 已使用长度 147 | alloc uint32 //总长度 148 | flag uint8 //低 3 位存类型,高 5 位预留 149 | buf []byte 150 | } 151 | type Sdshdr64 struct { 152 | len uint64 // 已使用长度 153 | alloc uint64 //总长度 154 | flag uint8 //低 3 位存类型,高 5 位预留 155 | buf []byte 156 | } 157 | -------------------------------------------------------------------------------- /adt/redisObject.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | import ( 4 | "errors" 5 | "unsafe" 6 | ) 7 | 8 | // type 9 | const ( 10 | RedisString = "string" // int embstr raw 11 | RedisList = "list" // ziplist linkedlist 12 | RedisHash = "hash" // ziplist ht 13 | RedisSet = "set" // intset ht 14 | RedisZset = "zset" // ziplist skiplist 15 | 16 | // encoding 17 | RedisEncodingInt = "int" 18 | RedisEncodingEmbstr = "embstr" 19 | RedisEncodingRaw = "raw" 20 | RedisEncodingHt = "hashtable" 21 | RedisEncodingLinkedlist = "linkedlist" 22 | RedisEncodingZiplist = "ziplist" 23 | RedisEncodingIntset = "intset" 24 | RedisEncodingSkiplist = "skiplist" 25 | ) 26 | 27 | type RedisObject struct { 28 | Ele unsafe.Pointer // 这里是指针 29 | types string 30 | encoding string 31 | *Object //这里指向了 object 指针 32 | lru int64 33 | } 34 | 35 | type Object struct { 36 | int 37 | Ele unsafe.Pointer 38 | *Dict 39 | *List 40 | int64 // 存储过期时间 41 | } 42 | 43 | func NewRedisObject() *RedisObject { 44 | obj := &RedisObject{} 45 | obj.Object = &Object{} 46 | return obj 47 | } 48 | func (obj *RedisObject) SetTypes(types string) *RedisObject { 49 | obj.types = types 50 | return obj 51 | } 52 | func (obj *RedisObject) SetEncoding(encoding string) *RedisObject { 53 | obj.encoding = encoding 54 | return obj 55 | } 56 | func (obj *RedisObject) SetPtr(ptr *Object) *RedisObject { 57 | obj.Object = ptr 58 | return obj 59 | } 60 | 61 | // 可以设置 int string 62 | func (obj *RedisObject) Set(ptr interface{}) *RedisObject { 63 | 64 | switch ptr.(type) { 65 | case *string: 66 | str := ptr.(*string) 67 | obj.Ele = SdsNewLen([]byte(*str)) 68 | obj.SetEncoding(RedisEncodingRaw).SetTypes(RedisString) 69 | case int: 70 | obj.int = ptr.(int) 71 | obj.SetEncoding(RedisEncodingInt).SetTypes(RedisString) 72 | case int64: 73 | obj.int64 = ptr.(int64) 74 | default: 75 | panic("error type") 76 | } 77 | 78 | return obj 79 | } 80 | 81 | // 可以设置 int string todo: 刚开始是 ziplist 后期改成 dict 82 | func (obj *RedisObject) Hset(filed interface{}, value interface{}) { 83 | 84 | obj.SetTypes(RedisHash).SetEncoding(RedisEncodingHt) 85 | 86 | filedObj := NewRedisObject() 87 | filedObj.Set(filed) 88 | 89 | vObj := NewRedisObject() 90 | vObj.Set(value) 91 | 92 | if obj.Dict != nil { 93 | obj.Dict.Hset(filedObj, vObj) 94 | } else { 95 | obj.Dict = NewDict().Hset(filedObj, vObj) 96 | } 97 | 98 | } 99 | 100 | func (obj *RedisObject) HGet(filed interface{}) (*RedisObject, error) { 101 | 102 | if obj.types != RedisHash { 103 | return nil, errors.New("type not redis_hash") 104 | } 105 | 106 | filedObj := NewRedisObject() 107 | filedObj.Set(filed) 108 | 109 | return obj.Dict.Hget(filedObj), nil 110 | } 111 | 112 | func (obj *RedisObject) RPush(strings []string) { 113 | obj.SetTypes(RedisList).SetEncoding(RedisEncodingLinkedlist) 114 | obj.List = NewList() 115 | 116 | for _, v := range strings { 117 | obj.List.RPush(NewRedisObject().Set(&v)) 118 | } 119 | } 120 | func (obj *RedisObject) RPop() *RedisObject { 121 | if obj.List == nil { 122 | return nil 123 | } 124 | 125 | return obj.List.RPop() 126 | } 127 | 128 | // 暂时未实现方法 129 | func (obj *RedisObject) Get() interface{} { 130 | 131 | switch obj.types { 132 | case RedisString: 133 | switch obj.encoding { 134 | case RedisEncodingInt: 135 | return obj.int 136 | default: 137 | return obj.Ele 138 | } 139 | default: 140 | return nil 141 | } 142 | 143 | } 144 | 145 | func (obj *RedisObject) GetType() string { 146 | return obj.types 147 | } 148 | 149 | func (obj *RedisObject) GetExpireSecond() int64 { 150 | return obj.int64 151 | } 152 | -------------------------------------------------------------------------------- /adt/dictHt.go: -------------------------------------------------------------------------------- 1 | package adt 2 | 3 | type DictHt struct { 4 | table []*DictEntry 5 | size uint64 6 | sizeMask uint64 7 | used uint64 8 | } 9 | 10 | func NewDictHt() *DictHt { 11 | return &DictHt{} 12 | } 13 | 14 | func (d *DictHt) InitHtBySize(size uint64) *DictHt { 15 | d.size = size 16 | d.used = 0 17 | d.sizeMask = size - 1 18 | 19 | d.table = make([]*DictEntry, int64(size)) 20 | return d 21 | } 22 | 23 | func (d *DictHt) AddDictValue(key *RedisObject, value *RedisObject) { 24 | 25 | // 根据 hash 算法获取 index 26 | index := d.GetIndex(d.GetHash(key)) 27 | 28 | // 查看是否 link 上有重复的 key 29 | if existsEntry := d.FindSameKey(index, key); existsEntry != nil { 30 | existsEntry.setValue(value) 31 | return 32 | } 33 | 34 | entry := NewDictEntry() 35 | entry.setKey(key).setValue(value) 36 | 37 | // hash 冲突了 38 | if d.IsHashConflict(index) { 39 | entry.next = d.table[index] 40 | d.table[index] = entry 41 | } else { 42 | d.table[index] = entry 43 | } 44 | 45 | d.IncrUsed() 46 | } 47 | 48 | func (d *DictHt) GetHash(key *RedisObject) (hashVal uint64) { 49 | 50 | bytePoint := (*[]byte)(key.Ele) 51 | k := string(*bytePoint) 52 | 53 | for _, v := range k { 54 | hashVal = (hashVal << 5) + uint64(v+1) 55 | } 56 | 57 | return hashVal 58 | } 59 | 60 | func (d *DictHt) GetIndex(hash uint64) uint64 { 61 | return hash & d.sizeMask 62 | } 63 | 64 | func (d *DictHt) IncrUsed() { 65 | d.used++ 66 | } 67 | 68 | func (d *DictHt) IsHashConflict(index uint64) bool { 69 | return d.table[index] != nil 70 | } 71 | 72 | func (d *DictHt) HasSameKey(index uint64, key *RedisObject) bool { 73 | return string(*(*[]byte)(d.table[index].key.Ele)) == string(*(*[]byte)(key.Ele)) 74 | } 75 | 76 | func (d *DictHt) FindSameKey(index uint64, key *RedisObject) *DictEntry { 77 | currentDictTable := d.table[index] 78 | 79 | for currentDictTable != nil { 80 | // 循环 当前 dict table 中是否存在 key 81 | if *(*string)(currentDictTable.key.Ele) == *(*string)(key.Ele) { 82 | return currentDictTable 83 | } 84 | currentDictTable = currentDictTable.next 85 | } 86 | 87 | return nil 88 | } 89 | 90 | func (d *DictHt) ShouldReHash() bool { 91 | return d.used >= d.size 92 | } 93 | 94 | func (d *DictHt) findValue(key *RedisObject) *RedisObject { 95 | 96 | index := d.GetIndex(d.GetHash(key)) 97 | 98 | if d.table == nil || d.table[index] == nil { 99 | return nil 100 | } 101 | 102 | if !d.IsLinked(index) { 103 | if string(*(*[]byte)(d.table[index].key.Ele)) == string(*(*[]byte)(key.Ele)) { 104 | return d.table[index].v 105 | } 106 | return nil 107 | } 108 | 109 | // next 有数据 110 | tempTable := d.table[index] 111 | 112 | for tempTable != nil { 113 | if string(*(*[]byte)(tempTable.key.Ele)) == string(*(*[]byte)(key.Ele)) { 114 | return tempTable.v 115 | } 116 | tempTable = tempTable.next 117 | } 118 | 119 | return nil 120 | } 121 | 122 | func (d *DictHt) delValue(key *RedisObject) int { 123 | 124 | index := d.GetIndex(d.GetHash(key)) 125 | if d.table == nil || d.table[index] == nil { 126 | return 0 127 | } 128 | 129 | // next 有数据 130 | tempTable := d.table[index] 131 | for tempTable != nil { 132 | if string(*(*[]byte)(tempTable.key.Ele)) == string(*(*[]byte)(key.Ele)) { 133 | //tempTable.v 134 | if tempTable.next != nil { 135 | d.table[index] = tempTable.next 136 | } 137 | 138 | tempTable.v = nil 139 | d.used-- 140 | } 141 | tempTable = tempTable.next 142 | } 143 | 144 | return 1 145 | } 146 | 147 | func (d *DictHt) IsLinked(index uint64) bool { 148 | return d.table[index].next != nil 149 | } 150 | 151 | func (d *DictHt) MoveTableToNewByIndex(i int64, ht *DictHt) int { 152 | 153 | j := 0 154 | // rehash 当前 treHashIndex 的数据 155 | for d.table[i] != nil { 156 | ht.AddDictValue(d.table[i].key, d.table[i].v) 157 | //ht.IncrUsed() 158 | j++ 159 | d.table[i] = d.table[i].next 160 | } 161 | return j 162 | } 163 | 164 | func (d *DictHt) FinishedReHash(i int64) bool { 165 | return d.size <= uint64(i) 166 | } 167 | func (d *DictHt) IsEmpty() bool { 168 | return d == nil || d.size <= 0 169 | } 170 | 171 | func (d *DictHt) CompareKey(a *RedisObject, b *RedisObject) bool { 172 | return string(*(*[]byte)(a.Ele)) == string(*(*[]byte)(b.Ele)) 173 | } 174 | -------------------------------------------------------------------------------- /server/redisDb.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "redis/adt" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | const NilString = "" 12 | 13 | type RedisDb struct { 14 | dict *adt.Dict // 数据库空间,保存所有键值对 15 | expire *adt.Dict // 过期字典,保存键过期时间 16 | } 17 | 18 | func NewRedisDb() *RedisDb { 19 | db := &RedisDb{} 20 | dict := adt.NewDict() 21 | db.dict = dict 22 | return db 23 | } 24 | 25 | func (r *RedisDb) Set(key, value string) error { 26 | 27 | k := adt.NewRedisObject().Set(&key) 28 | 29 | // 先找到 redisDb 中实际存值的 hash 30 | existsRedisObj := r.dict.Hget(k) 31 | 32 | if existsRedisObj != nil { 33 | if existsRedisObj.GetType() != adt.RedisString { 34 | errorString := fmt.Sprint("已存在类型为 ", existsRedisObj.GetType(), " 的 (", key, "), 不能设置为 ", adt.RedisString) 35 | return errors.New(errorString) 36 | } 37 | 38 | existsRedisObj.Hset(adt.NewRedisObject().Set(&key), adt.NewRedisObject().Set(&value)) 39 | } else { 40 | r.dict.Hset(adt.NewRedisObject().Set(&key), adt.NewRedisObject().Set(&value)) 41 | } 42 | 43 | return nil 44 | } 45 | 46 | func (r *RedisDb) Get(key string) string { 47 | tarObj := r.dict.Hget(adt.NewRedisObject().Set(&key)) 48 | if tarObj == nil { 49 | return NilString 50 | } 51 | return string(*(*[]byte)(tarObj.Ele)) 52 | } 53 | 54 | func (r *RedisDb) HSet(key, filed, value string) (err error) { 55 | 56 | k := adt.NewRedisObject().Set(&key) 57 | 58 | // 先找到 redisDb 中实际存值的 hash 59 | existsRedisObj := r.dict.Hget(k) 60 | 61 | if existsRedisObj != nil { 62 | if existsRedisObj.GetType() != adt.RedisHash { 63 | errorString := fmt.Sprint("已存在类型为 ", existsRedisObj.GetType(), " 的 (", key, "), 不能设置为 ", adt.RedisHash) 64 | return errors.New(errorString) 65 | } 66 | 67 | existsRedisObj.Hset(&filed, &value) 68 | } else { 69 | redisObj := adt.NewRedisObject() 70 | redisObj.Hset(&filed, &value) 71 | 72 | // 再把 key => dict 存入 r.dict 73 | r.dict = adt.NewDict().Hset(k, redisObj) 74 | } 75 | 76 | return nil 77 | } 78 | 79 | func (r *RedisDb) HGet(key, filed string) string { 80 | 81 | k := adt.NewRedisObject().Set(&key) 82 | f := adt.NewRedisObject().Set(&filed) 83 | existsRedisObj := r.dict.Hget(k) 84 | 85 | if existsRedisObj != nil { 86 | if existsRedisObj.GetType() != adt.RedisHash { 87 | return "can not use this get " + existsRedisObj.GetType() 88 | } 89 | 90 | targetObj := existsRedisObj.Hget(f) 91 | 92 | if targetObj == nil { 93 | return NilString 94 | } 95 | 96 | return string(*(*[]byte)(targetObj.Ele)) 97 | } 98 | 99 | return NilString 100 | } 101 | 102 | func (r *RedisDb) Del(key string) int { 103 | return r.dict.Hdel(adt.NewRedisObject().Set(&key)) 104 | } 105 | 106 | func (r *RedisDb) Hdel(key, filed string) int { 107 | 108 | k := adt.NewRedisObject().Set(&key) 109 | f := adt.NewRedisObject().Set(&filed) 110 | existsRedisObj := r.dict.Hget(k) 111 | 112 | if existsRedisObj != nil { 113 | if existsRedisObj.GetType() != adt.RedisHash { 114 | return 0 115 | } 116 | return existsRedisObj.Hdel(f) 117 | } 118 | 119 | return 0 120 | } 121 | 122 | func (r *RedisDb) RPush(key string, value ...string) { 123 | obj := adt.NewRedisObject() 124 | obj.RPush(value) 125 | 126 | r.dict.Hset(adt.NewRedisObject().Set(&key), obj) 127 | } 128 | 129 | func (r *RedisDb) RPop(key string) string { 130 | tarObj := r.dict.Hget(adt.NewRedisObject().Set(&key)) 131 | if tarObj == nil || tarObj.List.IsEmpty() { 132 | return "" 133 | } 134 | return string(*(*[]byte)(tarObj.List.RPop().Ele)) 135 | } 136 | func (r *RedisDb) Expire(key, value string) int { 137 | 138 | if r.expire == nil { 139 | dict := adt.NewDict() 140 | r.expire = dict 141 | } 142 | 143 | second, err := strconv.Atoi(value) 144 | if err != nil { 145 | return 0 146 | } 147 | 148 | i := time.Now().Unix() + int64(second) 149 | r.expire.Hset(adt.NewRedisObject().Set(&key), adt.NewRedisObject().Set(i)) 150 | return 1 151 | } 152 | 153 | func (r *RedisDb) ExpireIfNeeded(key string) bool { 154 | 155 | if r.expire == nil { 156 | return false 157 | } 158 | 159 | obj := r.expire.Hget(adt.NewRedisObject().Set(&key)) 160 | 161 | if obj == nil { 162 | return false 163 | } 164 | 165 | return obj.GetExpireSecond() < time.Now().Unix() 166 | } 167 | 168 | func (r *RedisDb) DelExpire(key string) int { 169 | return r.expire.Hdel(adt.NewRedisObject().Set(&key)) 170 | } 171 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------