Redis 基本介绍

Redis 的安装

windows版 https://github.com/tporadowski/redis/releases
Linux版 https://redis.io/download/#redis-downloads
解压即可
image.png

Redis 操作的基本原理

Redis 的使用 - 图2

Redis 的基本使用

Redis 安装好后,默认有 16 个数据库,初始默认使用 0 号库, 编号是 0…15

  1. 添加 key-val [set]
  2. 查看当前 redis 的 所有 key [keys *]
  3. 获取 key 对应的值. [get key]
  4. 切换 redis 数据库 [select index]
  5. 如何查看当前数据库的 key-val 数量 [dbsize]
  6. 清空当前数据库的 key-val 和清空所有数据库的 key-val [flushdb flushall]

image.png

Redis 的基本操作

Redis 的五大数据类型

Redis 的五大数据类型是: String(字符串) 、Hash (哈希)、List(列表)、Set(集合)和 zset(sorted set:有序集合)

String(字符串)

string 是 redis 最基本的类型,一个 key 对应一个 value。
string 类型是二进制安全的。除普通的字符串外,也可以存放图片等数据
redis 中字符串 value 最大是 512M

新增、修改、删除一个key

  1. # 添加一个元素
  2. 127.0.0.1:6379> set address beijing
  3. OK
  4. # 获取一个元素
  5. 127.0.0.1:6379> get address
  6. "beijing"
  7. # 因为key address已存在,set就变成了修改操作
  8. 127.0.0.1:6379> set address shanghai
  9. OK
  10. 127.0.0.1:6379> get address
  11. "shanghai"
  12. # 删除一个元素
  13. 127.0.0.1:6379> del address
  14. (integer) 1
  15. 127.0.0.1:6379> get address
  16. (nil)
  17. 127.0.0.1:6379>

设置一个10s过期的key

  1. # 给key msg 设置10s过期
  2. 127.0.0.1:6379> setex msg 10 hello
  3. OK
  4. 127.0.0.1:6379> get msg
  5. "hello"
  6. 127.0.0.1:6379> get msg
  7. "hello"
  8. # 10s后 key msg 就被干掉了
  9. 127.0.0.1:6379> get msg
  10. (nil)
  11. 127.0.0.1:6379>

同时操作一个或多个 key-value 对

# 同时设置多个键值对
127.0.0.1:6379> mset name1 tom name2 jack
OK
127.0.0.1:6379> get name1
"tom"
# 同时获取多个键值对
127.0.0.1:6379> mget name1 name2
1) "tom"
2) "jack"
127.0.0.1:6379>

Hash (哈希)

Redis hash 是一个键值对集合。
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。

存放一个 User 信息

# 给key user1 设置属性 name 为 tom
127.0.0.1:6379> hset user1 name tom
(integer) 1
# 给key user1 设置属性 age 为 10
127.0.0.1:6379> hset user1 age 10
(integer) 1
# 获取key user1 的属性 name
127.0.0.1:6379> hget user1 name
"tom"
# 获取key user1 的属性 age
127.0.0.1:6379> hget user1 age
"10"
# 获取key user1 的全部属性
127.0.0.1:6379> hgetall user1
1) "name"
2) "tom"
3) "age"
4) "10"
127.0.0.1:6379>

一次性来设置多个 filed 的值和返回多个 field 的值

# 给key user2 同时设置属性 name 为 jack age 为 20
127.0.0.1:6379> hmset user2 name jack age 20
OK
# 获取key user2 的属性 name age
127.0.0.1:6379> hmget user2 name age
1) "jack"
2) "20"
127.0.0.1:6379>

统计一个 hash 有几个元素

127.0.0.1:6379> hlen user2
(integer) 2

查看哈希表的指定字段是否存在

# 判断key user2是否存在属性 name
127.0.0.1:6379> hexists user2 name
# 存在
(integer) 1
# 判断key user2是否存在属性 job
127.0.0.1:6379> hexists user2 job
# 不存在
(integer) 0

List(列表)

列表是简单的字符串列表,按照插入顺序排序
你可以添加一个元素到列表的头部(左边)或者尾部(右边),如果值全移除,对应的键也就消失了
List 本质是个链表, List 的元素是有序的,元素的值可以重复

向list中新增、修改、删除多个key

# 向list左边添加元素
127.0.0.1:6379> lpush city beijing shanghai shenzen
(integer) 3
# 获取city list 中从0开始到结尾的数据
127.0.0.1:6379> lrange city 0 -1
1) "shenzen"
2) "shanghai"
3) "beijing"
# 向list右边添加元素
127.0.0.1:6379> rpush city hangzhou
(integer) 4
127.0.0.1:6379> lrange city 0 -1
1) "shenzen"
2) "shanghai"
3) "beijing"
4) "hangzhou"
# 将list左边的一个元素取出
127.0.0.1:6379> lpop city
"shenzen"
# 将list右边的一个元素取出
127.0.0.1:6379> rpop city
"hangzhou"
127.0.0.1:6379> lrange city 0 -1
1) "shanghai"
2) "beijing"
# 删除list city
127.0.0.1:6379> del city
(integer) 1
127.0.0.1:6379> lrange city 0 -1
(empty list or set)

按下标访问list元素

127.0.0.1:6379> lpush listTest a b
(integer) 2
# 访问下标为0的元素
127.0.0.1:6379> lindex listTest 0
"b"
# 访问下标为1的元素
127.0.0.1:6379> lindex listTest 1
"a"
# 访问下标为2的元素 没有返回nil
127.0.0.1:6379> lindex listTest 2
(nil)
127.0.0.1:6379>

获取list长度

127.0.0.1:6379> llen listTest
(integer) 2
127.0.0.1:6379> lpop listTest
"b"
127.0.0.1:6379> llen listTest
(integer) 1
127.0.0.1:6379>

Set(集合)

Redis 的 Set 是 string 类型的无序集合。
底层是 HashTable 数据结构, Set 也是存放很多字符串元素,字符串元素是无序的,而且元素的值不能重复

用set存放多个Email数据

# 添加两个邮箱
127.0.0.1:6379> sadd emails tom@qq.com jack@163.com
(integer) 2
# 访问emails中的元素
127.0.0.1:6379> smembers emails
1) "jack@163.com"
2) "tom@qq.com"
# 添加一个邮箱kevin@qq.com
127.0.0.1:6379> sadd emails kevin@qq.com
(integer) 1
# 查看
127.0.0.1:6379> smembers emails
1) "jack@163.com"
2) "tom@qq.com"
3) "kevin@qq.com"
# 再次添加一个重复邮箱kevin@qq.com
127.0.0.1:6379> sadd emails kevin@qq.com
# 无法添加
(integer) 0
# 查看
127.0.0.1:6379> smembers emails
1) "jack@163.com"
2) "tom@qq.com"
3) "kevin@qq.com"

sismember[判断值是否是成员] srem [删除指定值]

# 查看现有元素
127.0.0.1:6379> smembers emails
1) "jack@163.com"
2) "tom@qq.com"
3) "kevin@qq.com"
# 判断集合中是否有元素 tom@qq.com
127.0.0.1:6379> sismember emails tom@qq.com
# 有
(integer) 1
# 判断集合中是否有元素 tom~@qq.com
127.0.0.1:6379> sismember emails tom~@qq.com
# 没有
(integer) 0
# 删除元素 tom@qq.com
127.0.0.1:6379> srem emails tom@qq.com
(integer) 1
# 查看
127.0.0.1:6379> smembers emails
1) "jack@163.com"
2) "kevin@qq.com"

ZSet(有序集合)

Redis zset(有序集合)中的成员是有序排列的,集合中的每一个成员都是字符串类型,并且不允许重复
有序集合中每个成员都会关联一个 double(双精度浮点数)类型的 score (分数值),Redis 正是通过 score 实现了对集合成员的排序。
有序集合(zset)使用了两种不同的存储结构,分别是 zipList(压缩列表)和 skipList(跳跃列表)
注意:在有序集合中,成员是唯一存在的,但是分数(score)却可以重复。有序集合的最大的成员数为 2^32 - 1

保存员工薪水的有序集合

#在有序集合中添加一个成员
127.0.0.1:6379> ZADD salary 4000 lucy
(integer) 1
#同时添加多个成员
127.0.0.1:6379> ZADD salary 5000 tom 6000 Helen 6500.50 Jack 3000 Smith
(integer) 4
#查询指定区间上的元素
127.0.0.1:6379> ZRANGE salary 0 4
1) "Smith"
2) "lucy"
3) "tom"
4) "Helen"
5) "Jack"
#降序查看指定区间上的元素
127.0.0.1:6379> ZREVRANGE salary 0 4
1) "Jack"
2) "Helen"
3) "tom"
4) "lucy"
5) "Smith"
#查看指定元素的分值
127.0.0.1:6379> ZSCORE salary lucy
"4000"
#查看所有元素和分值
127.0.0.1:6379> ZRANGE salary 0 4 WITHSCORES
1) "Smith"
2) "3000"
3) "lucy"
4) "4000"
5) "tom"
6) "5000"
7) "Helen"
8) "6000"
9) "Jack"
10) "6500.5"
#统计指定工资范围内的元素个数3000<=score<=5000
127.0.0.1:6379> ZCOUNT salary 3000 5000
(integer) 3
#表示3000<score<5000
127.0.0.1:6379> ZCOUNT salary (3000 (5000
(integer) 1
#返回指定工资范围内的score和成员,限制条件是跳过1个元素,返回2个元素。
127.0.0.1:6379> ZRANGEBYSCORE salary 3000 6000 WITHSCORES LIMIT 1 2
1) "lucy"
2) "4000"
3) "tom"
4) "5000"
#查看有序集合在指定字典区间内的成员的数
#其中 - 表示最小值,而 + 则表示最大值
127.0.0.1:6379> ZLEXCOUNT salary - +
(integer) 5

Golang 操作 Redis

安装第三方开源Redis 库

  1. 使用第三方开源的 redis 库: github.com/garyburd/redigo/redis
  2. 在使用 Redis 前,先安装第三方 Redis 库,在 GOPATH 路径下执行安装指令: go get github.com/garyburd/redigo/redis
  3. 安装成功后,可以看到如下包

image.png
特别说明: 在安装 Redis 库前,确保已经安装并配置了 Git, 因为 是从 github 下载安装 Redis 库的, 需要使用到 Git。

Set/Get数据

package main
import (
    "fmt"
    "github.com/garyburd/redigo/redis" //引入redis包
)

func main() {
    //通过go 向redis 写入数据和读取数据
    //1. 链接到redis
    conn, err := redis.Dial("tcp", "127.0.0.1:6379")
    if err != nil {
        fmt.Println("redis.Dial err=", err)
        return 
    }
    defer conn.Close() //关闭..

    //2. 通过go 向redis写入数据 string [key-val]
    _, err = conn.Do("Set", "name", "tomjerry猫猫")
    if err != nil {
        fmt.Println("set  err=", err)
        return 
    }

    //3. 通过go 向redis读取数据 string [key-val]

    r, err := redis.String(conn.Do("Get", "name"))
    if err != nil {
        fmt.Println("set  err=", err)
        return 
    }

    //因为返回 r是 interface{}
    //因为 name 对应的值是string ,因此我们需要转换
    //nameString := r.(string)

    fmt.Println("操作ok ", r)
}
PS E:\GoProject> go run .\src\go_code\chapter18\redisdemo\main\       
操作ok  tomjerry猫猫

批量Set/Get 数据

_, err = c.Do("MSet", "name", "tom", "address", "北京")
r, err := redis.Strings(c.Do("MGet", "name", "address"))

for _, v := range r {
    fmt.Println(v)
}

给数据设置有效时间

//给 name 数据设置有效时间为 10s
_, err = c.Do("expire", "name", 10)

操作List

_, err = c.Do("lpush", "heroList", "no1:宋江", 30, "no2:卢俊义", 28) 
r, err := redis.String(c.Do("rpop", "heroList"))

Redis 链接池

通过 Golang 对 Redis 操作, 还可以通过 Redis 链接池, 流程如下

  1. 事先初始化一定数量的链接,放入到链接池
  2. 当 Go 需要操作 Redis 时,直接从 Redis 链接池取出链接即可
  3. 这样可以节省临时获取 Redis 链接的时间,从而提高效率

    链接池使用的案例

package main
import (
    "fmt"
    "github.com/garyburd/redigo/redis"
)

//定义一个全局的pool
var pool *redis.Pool

//当启动程序时,就初始化连接池
func init() {

    pool = &redis.Pool{
        MaxIdle: 8, //最大空闲链接数
        MaxActive: 0, // 表示和数据库的最大链接数, 0 表示没有限制
        IdleTimeout: 100, // 最大空闲时间
        Dial: func() (redis.Conn, error) { // 初始化链接的代码, 链接哪个ip的redis
        return redis.Dial("tcp", "localhost:6379")
        },
    }    

}

func main() {
    //先从pool 取出一个链接
    conn := pool.Get()
    defer conn.Close()

    _, err := conn.Do("Set", "name", "汤姆猫~~")
    if err != nil {
        fmt.Println("conn.Do err=", err)
        return
    }

    //取出
    r, err := redis.String(conn.Do("Get", "name"))
    if err != nil {
        fmt.Println("conn.Do err=", err)
        return
    }

    fmt.Println("r=", r)

    //如果我们要从pool 取出链接,一定保证链接池是没有关闭
    //pool.Close()
    conn2 := pool.Get()

    _, err = conn2.Do("Set", "name2", "汤姆猫~~2")
    if err != nil {
        fmt.Println("conn.Do err~~~~=", err)
        return
    }

    //取出
    r2, err := redis.String(conn2.Do("Get", "name2"))
    if err != nil {
        fmt.Println("conn.Do err=", err)
        return
    }

    fmt.Println("r=", r2)

    //fmt.Println("conn2=", conn2)
}