Skip to content

Commit ddcbee1

Browse files
committed
second commit 实现数据库内部数据结构以及若干相应redis命令
1 parent ed9c335 commit ddcbee1

21 files changed

+1005
-40
lines changed

database/command.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package database
2+
3+
import "redis-based-on-go/redis/protocol"
4+
5+
// commandTable 记录所有客户端命令
6+
var commandTable = make(map[string]*command)
7+
8+
// ExecFunc 命令执行函数
9+
type ExecFunc func(db *DB, args [][]byte) protocol.Reply
10+
11+
type command struct {
12+
name string
13+
executor ExecFunc
14+
}

database/concurrent_map.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package database
2+
3+
import (
4+
"sync"
5+
)
6+
7+
const (
8+
TABLE_COUNT = 16
9+
PRIME_32 = uint32(16777619)
10+
)
11+
12+
// DB 中 key-value 的并发安全结构
13+
type concurrentMap struct {
14+
tables []*table
15+
memberCount int
16+
tableCount int
17+
}
18+
19+
type table struct {
20+
m map[string]interface{}
21+
mutex sync.RWMutex
22+
}
23+
24+
// makeConcurrentMap makes a new concurrent map
25+
func makeConcurrentMap() *concurrentMap {
26+
cm := &concurrentMap{
27+
tables: make([]*table, TABLE_COUNT),
28+
tableCount: TABLE_COUNT,
29+
}
30+
for i := 0; i < TABLE_COUNT; i++ {
31+
cm.tables[i] = &table{
32+
m: make(map[string]interface{}),
33+
}
34+
}
35+
return cm
36+
}
37+
38+
// fnv32 hash function
39+
func fnv32(key string) uint32 {
40+
hash := uint32(2166136261)
41+
for i := 0; i < len(key); i++ {
42+
hash *= PRIME_32
43+
hash ^= uint32(key[i])
44+
}
45+
return hash
46+
}
47+
48+
// getTable returns the table of the given key
49+
func (cm *concurrentMap) getTable(key string) *table {
50+
return cm.tables[fnv32(key)%uint32(cm.tableCount)]
51+
}
52+
53+
// set 并发安全地修改 db 的 key -> value
54+
func (cm *concurrentMap) set(key string, value interface{}) int {
55+
table := cm.getTable(key)
56+
table.mutex.Lock()
57+
defer table.mutex.Unlock()
58+
_, ok := table.m[key]
59+
if ok {
60+
table.m[key] = value
61+
return 0
62+
} else {
63+
table.m[key] = value
64+
cm.memberCount++
65+
return 1
66+
}
67+
}
68+
69+
// get 并发安全地获取指定 key 的 value
70+
func (cm *concurrentMap) get(key string) (interface{}, bool) {
71+
table := cm.getTable(key)
72+
table.mutex.RLock()
73+
defer table.mutex.RUnlock()
74+
value, ok := table.m[key]
75+
if !ok {
76+
return nil, false
77+
} else {
78+
return value, true
79+
}
80+
}
81+
82+
// isExisted 并发安全地判断指定 key 是否存在
83+
func (cm *concurrentMap) isExisted(key string) bool {
84+
table := cm.getTable(key)
85+
table.mutex.RLock()
86+
defer table.mutex.RUnlock()
87+
_, ok := table.m[key]
88+
return ok
89+
}
90+
91+
// delete 并发安全地删除指定 key
92+
func (cm *concurrentMap) delete(key string) int {
93+
table := cm.getTable(key)
94+
table.mutex.Lock()
95+
defer table.mutex.Unlock()
96+
_, ok := table.m[key]
97+
if ok {
98+
delete(table.m, key)
99+
cm.memberCount--
100+
return 1
101+
} else {
102+
return 0
103+
}
104+
}

database/constants.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package database
2+
3+
const (
4+
INSUFFICIENT_PARAMETERS = "wrong number of arguments"
5+
REQUIRE_INTEGER = "require_integer"
6+
)

database/database.go

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,61 @@ package database
22

33
import (
44
"errors"
5-
"sync"
5+
"fmt"
6+
"redis-based-on-go/redis/protocol"
67
)
78

8-
// TODO 完善内存数据库与数据结构
9-
109
type DB struct {
11-
table sync.Map
10+
table *concurrentMap // key -> value value 为任意 reids 类型
1211
}
1312

1413
func NewDB() *DB {
15-
return &DB{}
14+
return &DB{table: makeConcurrentMap()}
1615
}
1716

18-
func (db *DB) Set(key, value string) int {
19-
_, ok := db.table.Load(key)
17+
// setData 修改 db 的 key -> value value 为任意 redis 类型 内部修改由各结构本身实现
18+
func (db *DB) setData(key string, value interface{}) int {
19+
_, ok := db.table.get(key)
2020
if ok {
21-
db.table.Delete(key)
22-
db.table.Store(key, value)
21+
db.table.delete(key)
22+
db.table.set(key, value)
2323
return 0
2424
} else {
25-
db.table.Store(key, value)
25+
db.table.set(key, value)
2626
return 1
2727
}
2828
}
2929

30-
func (db *DB) Get(key string) (string, error) {
31-
value, ok := db.table.Load(key)
30+
// getData 获取 指定 key 的 value
31+
func (db *DB) getData(key string) (interface{}, error) {
32+
value, ok := db.table.get(key)
33+
if !ok {
34+
return nil, errors.New(fmt.Sprintf("key '%s' not found", key))
35+
} else {
36+
return value, nil
37+
}
38+
}
39+
40+
// isExisted 判断指定 key 是否存在
41+
func (db *DB) isExisted(key string) bool {
42+
_, ok := db.table.get(key)
43+
return ok
44+
}
45+
46+
func InitDBCommand() {
47+
initStringCommand()
48+
initHashCommand()
49+
initSetCommand()
50+
initListCommand()
51+
initSortedSetCommand()
52+
}
53+
54+
// Execute 根据命令字段执行相应命令
55+
func (db *DB) Execute(args [][]byte) protocol.Reply {
56+
command, ok := commandTable[string(args[0])]
3257
if !ok {
33-
return "", errors.New("not found")
58+
return &protocol.Error{Data: fmt.Sprintf("command '%s' not found", args[0])}
3459
} else {
35-
return value.(string), nil
60+
return command.executor(db, args[1:])
3661
}
3762
}

database/hash_command.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package database
2+
3+
import (
4+
"fmt"
5+
"redis-based-on-go/redis/protocol"
6+
)
7+
8+
// HSET KEY_NAME FIELD VALUE
9+
// setHash 为哈希表中的字段赋值 若哈希表不存在则创建并进行 HSET 操作 若字段已经存在于哈希表中则覆盖
10+
func setHash(db *DB, args [][]byte) protocol.Reply {
11+
if len(args) < 3 || len(args)%2 != 1 {
12+
return &protocol.ARGUEMENTS_NUMBER_ERR_REPLY
13+
}
14+
var hash *Hash
15+
existed := db.isExisted(string(args[0]))
16+
if existed {
17+
data, err := db.getData(string(args[0]))
18+
if err != nil {
19+
return &protocol.Error{Data: err.Error()}
20+
}
21+
var ok bool
22+
hash, ok = data.(*Hash)
23+
if !ok {
24+
return &protocol.TYPE_ERR_REPLY
25+
}
26+
} else {
27+
hash = makeSimpleHash()
28+
}
29+
insertCount := 0
30+
for i := 0; i < len(args)/2; i++ {
31+
insertCount += hash.set(string(args[2*i+1]), string(args[2*i+2]))
32+
}
33+
db.setData(string(args[0]), hash)
34+
return &protocol.Integer{Data: insertCount}
35+
}
36+
37+
// HGET KEY_NAME FIELD_NAME
38+
// getHash 返回哈希表中指定字段的值
39+
func getHash(db *DB, args [][]byte) protocol.Reply {
40+
if len(args) < 2 {
41+
return &protocol.ARGUEMENTS_NUMBER_ERR_REPLY
42+
}
43+
data, err := db.getData(string(args[0]))
44+
if err != nil {
45+
return &protocol.Error{Data: err.Error()}
46+
} else {
47+
hash, ok := data.(*Hash)
48+
if !ok {
49+
return &protocol.TYPE_ERR_REPLY
50+
}
51+
value, ok := hash.table[string(args[1])]
52+
if !ok {
53+
return &protocol.Error{Data: fmt.Sprintf("hash field '%s' not found", args[1])}
54+
} else {
55+
return &protocol.BulkString{Data: value}
56+
}
57+
}
58+
}
59+
60+
// HGETALL KEY_NAME
61+
// getAllField 返回哈希表中所有的字段和值 若 key 不存在则返回空列表
62+
func getAllField(db *DB, args [][]byte) protocol.Reply {
63+
if len(args) != 1 {
64+
return &protocol.ARGUEMENTS_NUMBER_ERR_REPLY
65+
}
66+
data, err := db.getData(string(args[0]))
67+
if err != nil {
68+
return &protocol.EMPTY_ARRAY
69+
} else {
70+
hash, ok := data.(*Hash)
71+
if !ok {
72+
return &protocol.TYPE_ERR_REPLY
73+
}
74+
all := hash.getAll()
75+
replys := make([]protocol.Reply, len(all))
76+
for i, s := range all {
77+
replys[i] = &protocol.BulkString{Data: s}
78+
}
79+
return &protocol.Array{Data: replys}
80+
}
81+
}
82+
83+
func initHashCommand() {
84+
commandTable["hset"] = &command{name: "hset", executor: setHash}
85+
commandTable["hget"] = &command{name: "hget", executor: getHash}
86+
commandTable["hgetall"] = &command{name: "hgetall", executor: getAllField}
87+
}

database/hash_struct.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package database
2+
3+
type Hash struct {
4+
table map[string]string
5+
}
6+
7+
// HashConsumer 遍历函数
8+
type HashConsumer func(key, value string) bool
9+
10+
// MakeSimple makes a new hash table
11+
func makeSimpleHash() *Hash {
12+
return &Hash{
13+
table: make(map[string]string),
14+
}
15+
}
16+
17+
func (sh *Hash) get(field string) (string, bool) {
18+
value, ok := sh.table[field]
19+
return value, ok
20+
}
21+
22+
// 0 update 1 insert
23+
func (sh *Hash) set(field, value string) int {
24+
_, ok := sh.table[field]
25+
sh.table[field] = value
26+
if ok {
27+
return 0
28+
} else {
29+
return 1
30+
}
31+
}
32+
33+
func (sh *Hash) size() int {
34+
size := len(sh.table)
35+
return size
36+
}
37+
38+
func (sh *Hash) getAll() []string {
39+
all := make([]string, sh.size()*2)
40+
i := 0
41+
sh.forEach(func(key, value string) bool {
42+
all[i] = key
43+
all[i+1] = value
44+
i += 2
45+
return true
46+
})
47+
return all
48+
}
49+
50+
func (sh *Hash) forEach(consumer HashConsumer) {
51+
for key, value := range sh.table {
52+
consumer(key, value)
53+
}
54+
}

0 commit comments

Comments
 (0)