- 前言
- Go 与操作系统
- Go 内部机制
- Go 基本数据类型
- 4 组合类型的使用
- 5 数据结构
- 6 Go package 中不为人知的知识
- 7 反射和接口
- 8 Go UNIX 系统编程
- 08.1 关于 UNIX 进程
- 08.2 flag 包
- 8.2 flag 包
- 08.3 io.Reader 和 io.Writer 接口
- 08.4 bufio 包
- 08.5 读取文本文件
- 08.6 从文件中读取所需的数据量
- 08.7 为什么我们使用二进制格式
- 08.8 读取 CSV 文件
- 08.9 写入文件
- 08.10 从磁盘加载和保存数据
- 08.11 再看strings包
- 08.12 关于bytes包
- 08.13 文件权限
- 08.14 处理 Unix 信号
- 08.15 Unix 管道编程
- 08.16 遍历目录树
- 08.17 使用 ePBF
- 08.18 关于 syscall.PtraceRegs
- 08.19 跟踪系统调用
- 08.20 User ID 和 group ID
- 08.21 其他资源
- 08.22 练习
- 08.23 总结
- 9 并发 Goroutines、Channel 和 Pipeline
- 10 Go 并发-进阶讨论
- 11 代码测试、优化及分析
- 12 Go 网络编程基础
- 13 网络编程 - 构建服务器与客户端
04.8 实现简单的K-V存储
本节你将学习使用Go实现K-V存储的简单实现,其背后的思想是易于理解的:尽可能快速地发出请求并给出响应,并将其转化成对应的数据结构。
本节代码将实现K-V存储的四个基本功能:
- 添加新元素
- 基于key删除已有的元素
- 给定key查找对应value
- 修改key对应的value
我们将这四个功能命名为ADD
,DELETE
,LOOKUP
,CHANGE
,完成这四个基本功能,你将会对K-V存储的实现有一个全面的了解。另外,当你输入STOP
时整个程序就会停止,输入PRINT
命令就会打印出当前K-V存储的内容。
本节的keyValue.go
将分为5个代码段解释。
第一部分:
> package main
>
> import (
> "bufio"
> "fmt"
> "os"
> "strings"
> )
>
> type myElement struct {
> Name string
> SurName string
> Id string
> }
>
> var DATA = make(map[string]myElement)
我们使用原生的Go map来实现K-V存储,因为内置的数据结构往往执行效率更高。map
变量被声明为全局变量,其k为string
类型,v为myElement
类型,myElement
是自定义的结构体。
第二部分代码:
> func ADD(k string,n myElement) bool {
> if k == "" {
> return false
> }
> if LOOKUP(k) == nil {
> DATA[k] = n
> return true
> }
> return false
> }
>
> func DELETE(k string) bool {
> if LOOKUP(k) != nil {
> delete(DATA, k)
> return true
> }
> return false
> }
这部分代码实现了命令行ADD
和DELETE
,用户在执行ADD
命令时,如果没有携带足够的参数,我们要保证该操作不会失败,意味着myElement
中对应的字段为空字符串。然而如果你要添加的key已经存在了,就会报错(K-V存储不允许重复key出现)而不是修改对应的值。
第三部分代码:
> func LOOKUP(k string) *myElement {
> _, ok := DATA[k]
> if ok {
> n := DATA[k]
> return &n
> } else {
> return nil
> }
> }
>
> func CHANGE(k string, n myElement) bool {
> DATA[k] = n
> return true
> }
>
> func PRINT() {
> for k, v := range DATA {
> fmt.Printf("key: %s value: %v",k,v)
> }
> }
该代码段实现了LOOKUP
与CHANGE
功能,如果你要修改的key不存储,程序会自动将其存储。
PRINT
命令能够打印出目前所有K-V存储的内容。
第四部分代码:
> func main() {
> scanner := bufio.NewScanner(os.Stdin)
> for scanner.Scan() {
> text := scanner.Text()
> text = strings.TrimSpace(text)
> tokens := strings.Fields(text)
>
> switch len(tokens) {
> case 0:
> continue
> case 1:
> tokens = append(tokens,"")
> tokens = append(tokens,"")
> tokens = append(tokens,"")
> tokens = append(tokens,"")
> case 2:
> tokens = append(tokens,"")
> tokens = append(tokens,"")
> tokens = append(tokens,"")
> case 3:
> tokens = append(tokens,"")
> tokens = append(tokens,"")
> case 4:
> tokens = append(tokens,"")
>
> }
该部分代码读取用户的输入。首先,for
循环将保证程序一直等待用户的输入,接下来使用tokens
切片保证至少有5个元素的输入,即使只有ADD
命令需要5个参数。如果用户不想在使用ADD
命令时出现空字符串值,就需要这样输入:ADD aKey Field1 Field2 Field3
。
最后一部分代码:
> switch tokens[0] {
> case "PRINT":
> PRINT()
> case "STOP":
> return
> case "DELETE":
> if !DELETE(tokens[1]) {
> fmt.Println("Delete operations failed")
> }
> case "ADD":
> n := myElement{tokens[2],tokens[3],tokens[4]}
> if !ADD(tokens[1],n) {
> fmt.Println("Add operation failed")
> }
> case "LOOKUP":
> n := LOOKUP(tokens[1])
> if n != nil {
> fmt.Printf("%v\n",n)
> }
>
> case "CHANGE":
> n := myElement{tokens[2],tokens[3],tokens[4]}
> if !CHANGE(tokens[1],n) {
> fmt.Println("Update operation failed")
> }
>
> default:
> fmt.Println("Unknown command - please try again!")
>
> }
> }
> }
这部分代码处理用户的输入。switch
的使用使得程序的逻辑设计看上去十分清晰,能够将程序员从冗余的if...else
中拯救出来。
执行keyValue.go
得到如下输出:
> $ go run keyValue.go
> UNKNOWN
> Unknown command - please try again!
>
> ADD 123 1 2 3
> ADD 234 2 3 4
> ADD 234
> Add operation failed
> ADD 345
> PRINT
> key: 123 value: {1 2 3}key: 234 value: {2 3 4}key: 345 value: { }
> CHANGE 345 3 4 5
> PRINT
> key: 345 value: {3 4 5}key: 123 value: {1 2 3}key: 234 value: {2 3 4}
> DELETE 345
> PRINT
> key: 123 value: {1 2 3}key: 234 value: {2 3 4}
> ADD 567 -5 -6 -7
> PRINT
> key: 123 value: {1 2 3}key: 234 value: {2 3 4}key: 567 value: {-5 -6 -7}
> CHANGE 567
> PRINT
> key: 567 value: { }key: 123 value: {1 2 3}key: 234 value: {2 3 4}
> STOP
>
学习第8章
后你将学会如何支持K-V存储的数据持久化功能。然而,在单用户应用使用goroutines和channel是没有任何实际意义的,但是如果你是通过TCP/IP实现K-V存储,那么使用goroutines和channel会帮助你实现多连接、服务多用户功能。你将在第9、10章
学习goroutines和channel的知识,并且在12、13章
学习网络编程的相关知识,尽情期待吧!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论