Redis在Go中的应用

转自 http://witchiman.github.io/2017/04/14/go-redis

Go 作为一种新兴的语言,由于其本身在网络编程方面表现出来的优势,现在受到越来越多开发者们的青睐。不过,也正是因为它太新,相比于一些很成熟的语言,它的第三方库在质量和数量上并不占优势。相信随着时间的变化,Go程序员的群体日益庞大,这个问题最终会得以解决。

这里主要是介绍第三方库go-redis/redis在Go中的应用。redis是一个高性能的key-value数据库,这个不用多说,现在很多公司都在使用redis。Go语言官方已经提供了对redis的支持,但是没有redis client的官方实现,而go-redis/redis 是一个很好用的第三方库,目前托管在Github上,相比其它关于Go的redis client,上手更简单,虽然它提供的文档不够丰富,但是方法命名上保持了与redis原生命令的一致,见名知义, 稍微研究下就可以马上着手使用。

准备工作

首先去Github上把go-redis/redis 通过git clone下载下来,并放到指定的目录。

git clone git@github.com:go-redis/redis.git $GOPATH/src/github.com/go-redis/redis

导入要用到的包

import (
    "github.com/go-redis/redis"
    "fmt"
    "log"
)

如果是在本地测试,记得先打开redis-server。
现在连接到redis-server

client := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
        Password: "",      //默认空密码
        DB: 0,             //使用默认数据库
    })

defer client.Close()       //最后关闭

测试连接。如果pint通会收到返回的信息PONG

pong, err := client.Ping().Result()
if err != nil {
    log.Fatal(err)
}
fmt.Println("Connected result: ", pong)

常见数据类型的操作

strings的操作

String 是redis最基本的数据类型,一个key 对应 一个 value。value的最大数据长度是512M,只要不超过这个大小,可以直接存放任意格式的数据。

存储一个string

 client.Set("str1", "hello redis",0) //忽略错误

读取设定的值

str := client.Get("str1")
fmt.Println(str)

删除string

client.Del("strtest")

lists的操作

在redis里,list是简单的字符串列表,可以添加一个元素到列表的头部和尾部。

插入一个值,如果key不存在,新建一个list。

client.LPush("list","one","two","three") //rpush则在尾部插入

删除list中的值

client.LRem("list",2,"three") //删除list中前2个value为 ‘three’的元素
client.LPop("list") //删除头部的值,同理RPop删除尾部的值。

读取list的值。LRange方法第二个参数start是list读取开始的位置,第三个参数end是结束的位置,通过这两个参数设定操作的范围。如果第三个参数的值超过list的的长度,则redis会从list的头部开始遍历,直到读取 end – start +1 个值。

list, _ := client.LRange("list", 0, 2).Result()
fmt.Println("List: ", list)
//output:
//List:  [three two one]

client.LRem("list",1,"three")   //删除一个“three”
list, _ := client.LRange("list", 0, 2).Result()      
fmt.Println("List: ", list)  //从输出发现list中只剩下two 和one,遍历完list后又从头开始遍历再输出一个two
//output:
//List: [two one two]

hashes的操作

Hash 非常适合存储对象,比如用户的注册信息等等。一个hash 有多个字段,存储的时候根据需要设定相应字段的值。

存储hash

user := make(map[string]interface{})
user["name"] = "jim"
user["gender"] = "man"
user["age"] = 23
client.HMSet("user",user)

存取单个字段

client.HSet("user", "name","tom")
name := client.HGet("user","name")
fmt.Print(name)

获取整个hash

hash, _ := client.HGetAll("user").Result()
for k, v:= range hash{
    fmt.Printf("key: %v, value: %v ",k, v)
}
//output:
//key: name, value: jim key: age, value: 23 key: gender, value: man 

sets的操作

Set是没有排序的字符串集合,它的特点是里面不允许有重复的元素。还有一种有有序的集合,操作与set类似,只不过多了一个score的属性,通过这个属性可以获取指定顺序的集合。

新建一个set

client.SAdd("set", 7, 6, 5, 3)

获得set的元素数量

count := client.SCard("set")

获取整个set

nums:= client.SMembers("set")
fmt.Println("Set:", nums)
//output:
//Set: [3 5 6 7]

查看数据库中所有的key

result, _ := client.Keys("*").Result()
fmt.Println("Redis value: ", result)
//output:
//Redis value:  [pipe list str1 str2 set user]

管道和事务

管道

在redis中,管道(Pipleline)可以简单的理解为一系列命令的打包。通常,我们通过redis-cli与redis-server交互时,都是一个命令执行完后,明确收到redis-server的反馈信息时才进行下一个命令。这种交互是堵塞式的,效率比较低下。如:

    client: INCR X
    server: 1
    client: INCR X
    server: 2
    client: INCR X
    server: 3
    ...

使用管道后,多个命令可以放到一起一起执行,其实在管道中redis也是依次执行每个命令的,只是下一个命令不必等到上一个命令执行完反馈到client后再执行。

    client: INCR X
    client: INCR X
    client: INCR X 
    ...
    server: 1
    server: 2
    server: 3
    ...

使用管道。下面代码首先新建一个键 pipe,然后通过三个命令使它的值每次递增1,把这几个命令打包成进一个管道,然后一起执行。

pl := client.Pipeline()  
pl.Set("pipe", 0,0)
pl.Incr("pipe")
pl.Incr("pipe")
pl.Incr("pipe")
pl.Exec()
p,_ := client.Get("pipe").Result()
fmt.Println("Pipe: ",p)
//output:
//Piple: 3

事务

Redis也像其它数据库一样提供了事务机制,通过MULTI可以开启一个事务,通过EXEC提交一个事务,DISCARD则可以回滚一个操作,WATCH和UNWATCH可以监控和取消监控指定的key。事务与管理类似,也是多个命令的打包,然后放到一起执行。只不过只要有一个命令执行失败,所有操作都会回滚。在go-redis/redis中使用事务很简单。代码基本上与使用管道一样。

pl := client.TxPipeline()  //此处只需把Pipleline()换成TxPipleline()
pl.Set("pipe", 0,0)
pl.Incr("pipe")
pl.Incr("pipe")
pl.Incr("pipe")
pl.Exec()
p,_ := client.Get("pipe").Result()
fmt.Println("Pipe: ",p)
//output:
//Transaction: 3

订阅

Redis中的订阅是一种消息通信机制。订阅者订阅了频道后,只要发送者通过这个频道发送消息,所有的订阅者就会收到消息。

下列代码通过Publish()创建了名为mychannel 的频道并发布。接着,开启一个goroutine用来订阅这个频道,Subscribe()方法返回一个Pubsub,通过Receive()来接受发布者发布的消息。为了保证在主线程在结束前goroutine收到信息,创建了一个空的chan。

done := make(chan struct{})
client.Publish("mychannel", "hello budy!\n")
go func() {
    pubsub := client.Subscribe("mychannel")
    msg,_ := pubsub.Receive()
    fmt.Println("Receive from channel:", msg)
    done <- struct {}{}
}()

<-done 
    原文作者:witchiman
    原文地址: https://www.jianshu.com/p/4045a3721b3c
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞