介绍
REmote DIctionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的 key-value 存储系统。
Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。
它通常被称为数据结构服务器,因为值(value)可以是 字符串 (String), 哈希 (Hash), 列表 (list), 集合 (sets) 和 有序集合 (sorted sets)等类型。
Redis 优势:
- 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
- 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
Mac 安装 Redis
首先安装homebrew
,使用 brew install redis
安装即可。
- 如果需要后台运行 redis 服务,使用命令
brew services start redis
- 如果不需要后台服务,则使用命令
redis-server /usr/local/etc/redis.conf
要运行命令,可以直接到 /usr/local/bin
目录下,有
-
redis-server
服务器运行命令 -
redis-cli
运行客户端
可以直接运行 redis-server
打开服务。然后另外开一个终端,运行 redis-cli
运行服务端,在服务端中输入 quit
可以退出。
go 操作 Redis
go 操作 redis 的客户端包有多个比如 redigo、go-redis,github上 Star 最多的莫属 redigo。
github地址:https://github.com/gomodule/redigo
文档:https://godoc.org/github.com/garyburd/redigo/redis
连接
Conn 接口是与 Redis 协作的主要接口,可以使用 Dial, DialWithTimeout 或者 NewConn 函数来创建连接,当任务完成时,应用程序必须调用 Close 函数来完成操作。
c, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
fmt.Println("Connect to redis error", err)
return
}else {
fmt.Println("连接成功")
}
defer c.Close()
命令操作
可以通过使用 Conn 接口中的 do 方法执行 redis 命令,redis 命令链接:http://doc.redisfans.com/
get、set 操作:
// 写入数据,永不过期
_, err = c.Do("SET", "username", "nick")
if err != nil {
fmt.Println("redis set failed:", err)
}
// 写入数据,10s后过期
_, err = c.Do("SET", "password", "123456", "EX", "10")
if err != nil {
fmt.Println("redis set failed:", err)
}
// 读取数据
username, err := redis.String(c.Do("GET", "username"))
if err != nil {
fmt.Println("redis get failed:", err)
} else {
fmt.Printf("Got username %v \n", username)
}
// 读取数据
passwd, err := redis.String(c.Do("GET", "password"))
if err != nil {
fmt.Println("redis get failed:", err)
} else {
fmt.Printf("Got password %v \n", passwd)
}
批量获取mget、批量设置mset
// 批量读写
_, err = c.Do("MSET", "name", "nill","passwd", "123456")
if err != nil {
fmt.Println("redis mset error:", err)
}
res, err := redis.Strings(c.Do("MGET", "name","passwd"))
if err != nil {
fmt.Println("redis get error:", err)
} else {
res_type := reflect.TypeOf(res)
fmt.Printf("res type : %s \n", res_type)
fmt.Printf("MGET name: %s \n", res)
fmt.Println(len(res))
}
列表操作:
// 列表操作
c.Do("lpush", "redlist", "qqq")
c.Do("lpush", "redlist", "www")
c.Do("lpush", "redlist", "eee")
values, _ := redis.Values(c.Do("lrange", "redlist", "0", "2"))
for _, v := range values {
fmt.Println(string(v.([]byte)))
}
hash操作:
// hash 操作
_, err = c.Do("HSET", "student","name", "nill","passwd","123456")
if err != nil {
fmt.Println("redis mset error:", err)
}
resi, err := redis.Int64(c.Do("HGET", "student","passwd"))
if err != nil {
fmt.Println("redis HGET error:", err)
} else {
res_type := reflect.TypeOf(resi)
fmt.Printf("res type : %s \n", res_type)
fmt.Printf("res : %d \n", resi)
}
管道 Pipelining
管道操作可以理解为并发操作,并通过 Send(),Flush(),Receive() 三个方法实现。客户端可以使用 send() 方法一次性向服务器发送一个或多个命令,命令发送完毕时,使用 flush() 方法将缓冲区的命令输入一次性发送到服务器,客户端再使用 Receive() 方法依次按照先进先出的顺序读取所有命令操作结果。
c.Send("SET", "foo", "bar")//Send向连接的输出缓冲中写入命令
c.Send("GET", "foo")//
c.Flush()//Flush将连接的输出缓冲清空并写入服务器端
//Recevie按照FIFO顺序依次读取服务器的响应
c.Receive() // reply from SET
v, err = c.Receive() // reply from GET
Send:发送命令至缓冲区
Flush:清空缓冲区,将命令一次性发送至服务器
Recevie:依次读取服务器响应结果,当读取的命令未响应时,该操作会阻塞。
发布/订阅
redis 本身具有发布订阅的功能,其发布订阅功能通过命令 SUBSCRIBE(订阅)/PUBLISH(发布) 实现,并且发布订阅模式可以是多对多模式还可支持正则表达式,发布者可以向一个或多个频道发送消息,订阅者可订阅一个或者多个频道接受消息。
func subscribe() {
c, err := redis.Dial("tcp", "127.0.0.1:6379")
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
psc := redis.PubSubConn{c}
psc.Subscribe("redChatRoom")
for {
switch v := psc.Receive().(type) {
case redis.Message:
fmt.Printf("%s: message: %s\n", v.Channel, v.Data)
case redis.Subscription:
fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
case error:
fmt.Println(v)
return
}
}
}
func main() {
c, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
fmt.Println("Connect to redis error", err)
return
}else {
fmt.Println("连接成功")
}
defer c.Close()
go subscribe()
go subscribe()
for {
var s string
fmt.Scanln(&s)
_, err := c.Do("PUBLISH", "redChatRoom", s)
if err != nil {
fmt.Println("pub err: ", err)
return
}
}
}