Go Book / 2 Go Advances / 02 Go 数据库 I O(二): Redis操作

02 Go 数据库 I O(二): Redis操作

一、Redis概述

Redis是一个内存数据结构存储系统。它主要有以下特点:

  • 与其他数据库不同,它的数据主要存储在内存中,当然为了数据安全它也有磁盘存储备份。由于内存存储,所以它性能非常高。
  • 与其他K-V缓存系统不同,它提供多种数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) ,甚至提供Sub/Pub机制,你可以以此构建简单的消息队列。

Redis中文网提供了完整的Redis命令文档,你可以进入学习相关的常用命令。

二、Go 使用Redis

Go中对redis的支持一般都使用第三方包,这里我们使用redigo,其github star 6k,有较高的使用或支持率,维护比较稳定,不用担心有大坑。

安装:

go get -u github.com/gomodule/redigo/redis

1.redigo的简单使用

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
	"strconv"
	"time"
)

func BaseRedis() {
    //go 连接Redis操作,类似拨号连接tcp,其端口号默认6379
	conn, err := redis.Dial("tcp", "localhost:6379")
	ErrorHandler(err, "redis.Dial")
	defer conn.Close()

	//执行一项操作时如果知道值返回类型可用以下写法
	//一般Set类的设置值操作成功都返回"OK"
	//Get类操作返回Int、String、Strings、Bool、Bytes、ByteSlices,Int等等,可先输出调试再用特定类型接收

	//Set结果的原始类型string,值为OK
	reply1, err := redis.String(conn.Do("Set", "name", "fun"))
	ErrorHandler(err, "conn.Do:Set")

	//Get结果的原始类型[]uint8,值为fun
	reply2, err := redis.String(conn.Do("Get", "name"))
	ErrorHandler(err, "conn.Do:Get")

	//setex结果的原始类型string,值为OK
	reply3, err := conn.Do("setex", "myname", "60", "bill")
	ErrorHandler(err, "conn.Do:setex")

	reply4, err := conn.Do("persist", "myname")
	ErrorHandler(err, "conn.Do:persist")

	reply5, err := conn.Do("mset", "age", "60", "gender", "male")
	ErrorHandler(err, "conn.Do:mset")
	
	reply6, err := conn.Do("hmset", "bangzhu", "name", "jobs", "age", 10000)
	ErrorHandler(err, "conn.Do:hmset")
	
	reply7, err := conn.Do("hgetall", "bangzhu")
	ErrorHandler(err, "conn.Do:hgetall")
	
	reply8, err := conn.Do("rpush", "mydearlist", 11, 22, 33)
	ErrorHandler(err, "conn.Do:rpush")
	
	reply9, err := conn.Do("lrange", "fucklist", 0, -1)
	ErrorHandler(err, "conn.Do:lrange")
	
	reply10, err := conn.Do("sadd", "mydearset", 1, 2, 3)
	ErrorHandler(err, "conn.Do:sadd")
	
	reply11, err := conn.Do("smembers", "mydearset")
	ErrorHandler(err, "conn.Do:smembers")
	
	reply12, err := conn.Do("zadd", "mz", 10, "bill", 9, "bangzhu", 8, "zuckberg")
	ErrorHandler(err, "conn.Do:zadd")

	reply13, err := conn.Do("zrange", "mz", 0, -1)
	ErrorHandler(err, "conn.Do:zrange")
	
	//输出执行结果
	fmt.Printf("Set结果的原始类型%T,值为%v\n", reply1, reply1)
	fmt.Printf("Get结果的原始类型%T,值为%v\n", reply2, reply2)
	fmt.Printf("Setex结果的原始类型%T,值为%v\n", reply3, reply3)
	fmt.Printf("persist结果的原始类型%T,值为%v\n", reply4, reply4)
	fmt.Printf("mset结果的原始类型%T,值为%v\n", reply5, reply5)
	fmt.Printf("hmset结果的原始类型%T,值为%v\n", reply6, reply6)
	fmt.Printf("hgetall结果的原始类型%T,值为%s\n", reply7, reply7)
	fmt.Printf("rpush结果的原始类型%T,值为%v\n", reply8, reply8)
	fmt.Printf("lrange结果的原始类型%T,值为%v\n", reply9, reply9)
	fmt.Printf("sadd结果的原始类型%T,值为%v\n", reply10, reply10)
	fmt.Printf("smembers结果的原始类型%T,值为%s\n", reply11, reply11)
	fmt.Printf("zadd结果的原始类型%T,值为%v\n", reply12, reply12)
	fmt.Printf("zrange结果的原始类型%T,值为%s\n", reply13, reply13)


	//输出执行结果
    //Set结果的原始类型string,值为OK
    //Get结果的原始类型string,值为fun
    //Setex结果的原始类型string,值为OK
    //persist结果的原始类型int64,值为1
    //mset结果的原始类型string,值为OK
    //hmset结果的原始类型string,值为OK
    //hgetall结果的原始类型[]interface {},值为[name jobs age 10000]
    //rpush结果的原始类型int64,值为21
    //lrange结果的原始类型[]interface {},值为[]
    //sadd结果的原始类型int64,值为0
    //smembers结果的原始类型[]interface {},值为[1 2 3]
    //zadd结果的原始类型int64,值为0
    //zrange结果的原始类型[]interface {},值为[zuckberg bangzhu bill]
    //zrange结果的原始类型[]interface {},值为[zuckberg bangzhu bill]
    
}

redigo内置连接池的实现:以下示例演示开启一个连接池最高活动连接数为50,最大闲置连接数为20。开启100个协程从连接池获取连接并执行命令。

//go redis 连接池
func BaseRedisPoll() {

	//配置并获得一个连接池对象的指针
	poolPtr := &redis.Pool{
		//最大活动链接数。0为无限
		MaxActive: 50,

		//最大闲置链接数,0为无限
		MaxIdle: 20,

		//闲置链接超时时间
		IdleTimeout: time.Second * 100,

		Dial: func() (redis.Conn, error) {
			conn, e := redis.Dial("tcp", "localhost:6379")
			return conn, e
		}}

	defer poolPtr.Close()

	for i := 1; i <= 100; i++ {
		//并发100写入redis
		go func(pool *redis.Pool, n int) {
			//从连接池中获取一个连接
			conn := poolPtr.Get()
			//协程执行完成后释放连接
			defer conn.Close()

			key := "name" + strconv.Itoa(n)
			fmt.Println(key)
			_, err := redis.String(conn.Do("Set", key, n))
			ErrorHandler(err, "conn.Do")
			//fmt.Println(res)

		}(poolPtr, i)
	}

	//为了让协程有足够时间执行,保持主协程存活,
	//Tips:主协程死亡后协程也不存在!
	time.Sleep(3 * time.Second)

	fmt.Println("并发连接池执行完成!")

}

至此Redis的基本使用到此结束,更多使用方式会在实战篇展开。Redis常被用作数据层之上的缓存层,搭建redis缓存层可缓解数据库IO阻塞,从而加大系统地访问性能。另一个使用场景Redis也可以被设计成简单的Sub/Pub消息队列,以达到业务异步IO的功能。