golang redis数据库基本操作笔记

一、基础知识

Redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库。
Redis 优势

  • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
  • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  • 原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

选用Redis场景:

  • 解决应用服务器的cpu和内存压力
  • 减少io的读操作,减轻io的压力
  • 关系型数据库的扩展性不强,难以改变表结构
  • nosql数据库没有关联关系,数据结构简单,拓展表比较容易
  • nosql读取速度快,对较大数据处理快
  • 数据高并发的读写
  • 海量数据的读写
  • 对扩展性要求高的数据

Redis八大应用场景:
1、缓存
在目前的互联网网站中,缓存几乎是网站都在用的,合理的使用缓存不但可以提升网站访问速度,还可以大大降低数据库的压力。Redis不仅提供了键过期功能,也提供了灵活的键淘汰策略,而且拥有相比memcached更丰富的数据类型。所以,现在Redis用在缓存的场合非常多。

2、排行榜
很多网站都有排行榜的展示,如天猫的月度销量榜单、商品按时间的上新排行榜等。使用Redis提供的有序集合数据结构能方便的实现各种复杂的排行榜。

3、计数器
计数器就是像电商网站商品的浏览量、视频网站视频的播放数等等。为了保证数据实时效,每次浏览都得给+1,如果使用数据库存储,那么并发量高时如果每次都请求数据库的压力会比较大。可以使用Redis提供的incr命令来实现计数器功能,内存操作,性能非常好,非常适用于这些计数场景。

4、最新列表
Redis列表LIST结构,LPUSH可以在列表头部插入一个内容ID作为关键字,LTRIM用来修建LIST以限制LIST的长度,这样列表永远为N个ID,无需查询最新的列表,直接根据ID去到对应的内容页即可。

5、分布式Session
前面的文章讲过,可以使用Redis做session管理,以实现分布式下的session共享。

6、分布式锁
在很多互联网公司中都使用了分布式技术,分布式技术带来的技术挑战是对同一个资源的并发访问,如全局ID、减库存、秒杀等场景,并发量不大的场景可以使用数据库的悲观锁、乐观锁来实现,但在并发量高的场合中,利用数据库锁来控制资源的并发访问是不太理想的,大大影响了数据库的性能。可以利用Redis的setnx功能来编写分布式的锁,如果设置返回1说明获取锁成功,否则获取锁失败,实际应用中要考虑的细节要更多。

7、 社交网络
点赞、踩、关注/被关注、共同好友等是社交网站的基本功能,社交网站的访问量通常来说比较大,而且传统的关系数据库类型不适合存储这种类型的数据,Redis提供的哈希、集合等数据结构能很方便的的实现这些功能。

8、消息系统
消息队列是大型网站必用中间件,如RocketMQ、RabbitMQ、Kafka等流行的消息队列中间件,主要用于业务解耦、流量削峰及异步处理实时性低的业务。Redis提供了发布/订阅及阻塞队列功能,能实现一个简单的消息队列系统。另外,这个不能和专业的消息中间件相比。

二、基本操作:

在开发过程中我们使用到了开源库redis如下

github地址

https://github.com/garyburd/redigo

文档地址:

http://godoc.org/github.com/garyburd/redigo/redis
1、数据库的连接

func connRedis() (c redis.Conn, err error) {  
	db, err := redis.Dial("tcp", "127.0.0.1:6379")  
	if err != nil {    
		fmt.Println("Connect to redis fail!", err)    
		return 
	}  
	return db, err
}

采用连接池的方式:

var RedisClient *redis.Pool

func ConnectRedis() {
	host := beego.AppConfig.DefaultString("redis.host", "127.0.0.1:6379")//通过beego配置文件获取
	pwd := beego.AppConfig.DefaultString("redis.psw", "")
	MaxIdle := beego.AppConfig.DefaultInt("redis.MaxIdle", 100)
	MaxActive := beego.AppConfig.DefaultInt("redis.MaxActive", 1024)
	RedisClient = &redis.Pool{
		// 从配置文件获取maxidle以及maxactive,取不到则用后面的默认值
		MaxIdle:     MaxIdle,
		MaxActive:   MaxActive,
		IdleTimeout: 180 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", host, redis.DialPassword(pwd))
			if err != nil {
				return nil, err
			}
			return c, nil
		},
	}

}

2、写入

func saveToRedis(c redis.Conn) { 
 	_, err := c.Do("SET", "name", "TigerwolfC", "EX", "60") 
	 if err != nil {  
   		fmt.Println("redis set failed:", err)  
   	} else {    
   		fmt.Println("save success")  
   }
}

批量写入

 _, err := c.Do("MSET", "name", "TigerwolfC", "SEX", "F", "EX", "60")  
 if err != nil {    
	 fmt.Println("redis set failed:", err)  
 } else {    
	 fmt.Println("save success")  
 } 

tips:EX是这个值的过期时间

连接池方式写入

type UserInfo struct {
	Name        string        //邮箱号,如"455*****"
	Displayname string   //用户名,如“张三”
	Mail        string          //邮箱,如“455*****@qq.com.cn”
}

func saveToRedis(userInfos map[int]models.UserInfo) error { 
	rcc := RedisClient.Get()
	_, err := rcc.Do("SELECT", 1)
	if err != nil {
		return err
	}
	for _, v := range userInfos {
		_, err = rcc.Do("HMSET", v.Name, "mail", v.Mail, "displayName", v.Displayname)
		if err != nil {
			return err
		}
	}

	return nil
}

3、读取

func readFromRedis(c redis.Conn) {  
	username, err := redis.String(c.Do("GET", "name"))  
	if err != nil {    
		fmt.Println("redis get failed:", err)  
	} else {    
		fmt.Printf("Get mykey: %v \n", username)  
	} 
}

批量读取

func readFromRedis(c redis.Conn) {  
	username, err := redis.Strings(c.Do("MGET", "SEX", "name"))  
	if err != nil {    
		fmt.Println("redis get failed:", err)  
	} else {    
		fmt.Printf("Get mykey: %v \n", username)  
	} 
}

4、删除

func delFromRedis(c redis.Conn) { 
	 _, err := c.Do("DEL", "name", "SEX")  
	 if err != nil {    
		 fmt.Println("redis delete failed:", err) 
 	 } else {    
 		 fmt.Println("delete success")  
 	 }
  }

一些业务中无效key数量比较多,需要脚本批量删除,利用MULTI批量删除
批量删除

conn, err := redis.Dial("tcp", "127.0.0.1:6379")
    if err != nil {
        panic(err)
    }
    defer conn.Close()
    conn.Do("SET", "app1", "test1")
    conn.Do("SET", "app2", "test2")
    val, err := redis.Strings(conn.Do("KEYS", "app*"))
    fmt.Println(val, err)
    conn.Send("MULTI")
    for i, _ := range val {
        conn.Send("DEL", val[i])
    }
    fmt.Println(conn.Do("EXEC"))

封装成函数

func DeleteRedis(arr []string) (res string, err error) {
    rcc := RedisClient.Get()
    rcc.Send("MULTI")
    for _, v := range arr {
        if len(v) == 0 {
            continue
        }
        arr := strings.Fields(v)
        rcc.Send("DEL", arr[1])
        res += arr[1] + " "
    }
    _, err = rcc .Do("EXEC")
    rcc.Close()
    return
}

5、设置keys 过期时间

在写入的时候如果设置了EX的时间,则当前的key过期时间为设置时间,不设置则当前的key永久有效

6、检查是否存在key值

exists, err := redis.Bool(conn.Do("EXISTS", "test-Key"))
if err != nil {
	fmt.Println("illegal exception")
}
fmt.Printf("exists or not: %v \n", exists)

7、读写json到redis

写json

func saveJsonDataToRedis(c redis.Conn) {  
	imap := map[string]string{"name": "TigerwolfC", "phone": "156**********"}  
	value, _ := json.Marshal(imap)  
	n, err := c.Do("SETNX", "chen_peggy", value) 
 	if err != nil {    
 		fmt.Println(err)  
 	} 	
   if n == int64(1) {    
 	 fmt.Println("success")  
   }
 } 

读json

func readJsonFromRedis(c redis.Conn) {  
 	 var imapGet map[string]string  
	 valueGet, err := redis.Bytes(c.Do("GET", "chen_peggy"))  
	 if err != nil {    
 		fmt.Println(err)  
 	}   
 	 errShal := json.Unmarshal(valueGet, &imapGet)  
	 if errShal != nil {    
 		fmt.Println(err)  
	 }  
	 fmt.Println(imapGet["name"])  
 	 fmt.Println(imapGet["phone"])
 }

8、列表操作,存入一组数据

存列表

func saveListToRedis(c redis.Conn) {  
	_, err := c.Do("lpush", "username", "chen_peggy")  
	if err != nil {    
		fmt.Println("redis set failed:", err)  
	}   
	_, err = c.Do("lpush", "username", "lisi")  
	if err != nil {    
		fmt.Println("redis set failed:", err)  
	} 
	 _, err = c.Do("lpush", "username", "TigerwolfC")  
 	if err != nil {    
		 fmt.Println("redis set failed:", err)  
 	}
 } 

读列表

 func readListFromRedis(c redis.Conn) {  
 	values, _ := redis.Values(c.Do("lrange", "username", "0", "2"))  
 	fmt.Printf("count%d", len(values))  
 	for _, v := range values {   
 		fmt.Println(string(v.([]byte)))  
  	}
  }

9、自增实现计数
redis中的INCR命令每次对key记录增加1,将 key 中储存的数字值增一。

如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。

如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。

本操作的值限制在 64 位(bit)有符号数字表示之内

127.0.0.1:6379> incr  TigerwolfC
 1
127.0.0.1:6379> incr  TigerwolfC 
 2
127.0.0.1:6379> incr  TigerwolfC
 3 
127.0.0.1:6379>

例子

package main

import (
    "log"
    "github.com/garyburd/redigo/redis"
)

func main() {
    server := "127.0.0.1:6379"
    option := redis.DialPassword("******")
    c, err := redis.Dial("tcp", server, option)
    if err != nil {
        log.Println("connect server failed:", err)
        return
    }
    defer c.Close()
    v, err := redis.Int64(c.Do("INCR", "TigerwolfC"))
    if err != nil {
        log.Println("INCR failed:", err)
        return
    }
    log.Println("value:", v)

}

output:
2018/10/26 12:30:56 value:4

计数器是 Redis 的原子性自增操作可实现的最直观的模式了,它的想法相当简单:每当某个操作发生时,向 Redis 发送一个 INCR 命令。
比如在一个 web 应用程序中,如果想知道用户在一年中每天的点击量,那么只要将用户 ID 以及相关的日期信息作为键,并在每次用户点击页面时,执行一次自增操作即可。
比如用户名是 TigerwolfC,点击时间是 2018 年 10 月 22 日,那么执行命令 INCR TigerwolfC::2018.10.22 。
可以用以下几种方式扩展这个简单的模式:

  1. 可以通过组合使用 INCR 和 EXPIRE ,来达到只在规定的生存时间内进行计数(counting)的目的。
  2. 客户端可以通过使用 GETSET 命令原子性地获取计数器的当前值并将计数器清零,更多信息请参考 GETSET 命令。
  3. 使用其他自增/自减操作,比如 DECR 和 INCRBY,用户可以通过执行不同的操作增加或减少计数器的值,比如在游戏中的记分器就可能用到这些命令。

你可能感兴趣的:(Redis,golang,redis,数据库,自增,应用场景)