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 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容