Redis学习(一):Redis 入门

4,183 阅读13分钟

1. Redis初识

  • 高性能Key-Value服务器
  • 多种数据结构
  • 丰富的功能
  • 高可用分布式支持

1. Redis是什么

  • 开源
  • 基于键值的存储服务系统
  • 多种数据结构
  • 高性能,功能丰富

2. Redis特性

  • 速度快
  • 持久化
  • 多种数据结构
  • 支持多种编程语言
  • 功能丰富
  • 简单
  • 主从复制
  • 高可用,分布式

3. Redis特性-速度快

  • 10W OPS
  • 数据存在哪 - 内存
  • 什么语言写 - C语言
  • 线程模型 - 单线程

4. Redis特性-持久化(断电不丢数据)

  • Redis所有数据保持在内存中,对数据的更新将异步地保存到硬盘上。

5. Redis特性-多种数据结构

  • String/Blobs/Bitmaps
  • Hash Tables(Object)
  • Linked Lists
  • Sets
  • Sorted Sets

6. Redis特性-功能丰富

  • 发布订阅
  • Lua脚本
  • 事务
  • pipeline

7. Redis典型使用场景

  • 缓存系统
  • 计数器
  • 消息队列系统
  • 排行榜
  • 社交网络
  • 实时系统

8. Redis安装,启动

1. Redis可执行文件说明

  • redis-server -> Redis服务器
  • redis-cli -> Redis命令行客户端
  • redis-benchmark -> Redis性能测试工具
  • redis-check-aof -> AOF文件修复工具
  • redis-check-dump -> RDB文件检查工具
  • redis-sentinel -> Sentinel服务器(2.8以后)

2. Redis可执行文件说明

  • 三种启动方式
    • 最简启动
    • 动态参数启动
    • 配置文件启动
  • 三种启动方式比较
    • 生产环境选择配置启动
    • 单机多实例配置文件可以用端口区分开

3. 验证

  • ps -ef | grep redis
  • netstat -antpl | grep redis
  • redis -cli -h ip -p port -ping

9. Redis常用配置

  • daemonize -> 是否是守护进程
  • port -> Redis对外端口号
  • logfile -> Redis系统日志
  • dir -> Redis工作目录

10. 使用配置文件启动

[root@localhost redis-4.0.5]# mkdir config
[root@localhost redis-4.0.5]# cp redis.conf config/
  • 去掉所有注释和空格并重定向配置。
[root@localhost config]# cat redis.conf | grep -v "#" | grep -v "^$" > redis-6382.conf
  • 进入配置文件删除目前不需要配置,留下基础配置
[root@localhost config]# vi redis-6382.conf

port 6382
dir "/usr/local/redis/data"
logfile "6382.log"
daemonize yes
  • 启动客户端并查看日志
[root@localhost redis]# redis-server config/redis-6382.conf

[root@localhost redis]# ps -ef | grep redis-server | grep 6382
root       8447      1  0 09:49 ?        00:00:00 redis-server *:6382

[root@localhost redis]# cd data
[root@localhost data]# ll
总用量 4
-rw-r--r--. 1 root root 1354 12月 20 09:49 6382.log

2. API的理解和使用

1. 通用命令

  • keys:遍历所有key。
    • 一般不在生产环境使用。
    • keys怎么用:热备从节点。scan。
  • dbsize:计算key的总数。
  • exists key:检查key是否存在。
  • del key[key ...]:删除指定key - value
  • expire key seconds:key在seconds秒后过期。
    • ttl key:查看key剩余的过期时间。
    • persist key:去掉key的过期时间。
  • type key:返回key的类型。
命令 时间复杂度
keys O(n)
dbsize O(1)
del O(1)
exists O(1)
expire O(1)
type O(1)

2. 数据结构和内部编码

数据结构和内部编码

  • 数据结构有不同的实现方法:内存比较昂贵,在不同情况下可能使用不同的内部编码来达到时间换空间和空间换时间的效果。
  • 实际上对于redis的源码内部有一个redisObject这样一个对象,或者说结构体,它会有很多属性:
    • 数据类型(type)
    • 编码方式(encoding)
    • 数据指针(ptr)
    • 虚拟内存(vm)
    • 其他信息
  • 用户不需要知道内部编码,其实也是面向接口编程的一种实践。

3. 单线程

单线程1

  • redis在一个瞬间只会执行一个命令。
  • 单线程为什么这么快?
    • 纯内存
    • 非阻塞IO
    • 避免线程切换和静态消耗
  • 单线程要注意什么?
    • 一次只运行一条命令
    • 拒绝长(慢)命令:keys,flushall,flushdb,slow lua script,mutil/exec,operate big value(collection)
    • 其实不是单线程:fysnc file descriptor,close file descriptor

4. 字符串

1. 结构和命令

  • 使用场景:
    • 缓存
    • 计数器
    • 分布式锁
  • get set del
    • get key:获取key对应的value
    • set key value:设置key - value
    • del key:删除key - value
  • incr decr incrby decrby
    • incr key:key自增1,如果key不存在,自增后get(key)=1
    • decr key:key自减1,如果key不存在,自增后get(key)=-1
    • incrby key k:key自增k,如果key不存在,自增后get(key)=k
    • decrby key k:key自减k,如果key不存在,自增后get(key)=-k
  • set setnx set xx
    • set key value:不管key是否存在,都设置
    • setnx key value:key不存在,才设置
    • set key value xx:key存在,才设置
  • mget mset
    • mget key1 key2 key3...:批量获取key,原子操作
    • mset key1 value1 key2 value2 key3 value3:批量设置key-value

2. 快速实战

  • 实现如下功能:记录网站每个用户个人主页的访问量
    • incr userid:pageview(单线程:无竞争)
  • 实现如下功能:缓存视频的基本信息(数据源在MySQL中)伪代码
public VideoInfo get(long id) {
    String redisKey = redisPrefix + id;
    VideoInfo viderInfo = redis.get(redisKey);
    if (videoInfo == null) {
        videoInfo = mysql.get(id);
        if (videoInfo != null) {
            // 序列化
            redis.set(redisKey, serialize(videoInfo));
        }
    }
    return videoInfo;
}
  • 实现如下功能:分布式id生成器
    • incr id(原子操作)

3. 查漏补缺

  • getset append strlen
    • getset key newvalue:set key newvalue并返回旧的value
    • append key value:将value追加到旧的value
    • strlen key:返回字符串的长度
  • incrbyfloat getrange setrange
    • incrybyfloat key 3.5:增加key对应的值3.5
    • getrange:获取字符串指定下标所有的值
    • setrange:设置指定下标所有对应的值
命令 含义 时间复杂度
set key value 设置key - value O(1)
get key value 获取key - value O(1)
del key value 删除key - value O(1)
setnx set xx 根据key是否存在设置key - value O(1)
incr decr 计数 O(1)
mget mset 批量操作key - value O(n)

5. 哈希

1. 结构和命令

  • 所有哈希的命令都是以H开头
  • hget hset hdel
    • hget key field:获取hash key对应的field的value
    • hget key field value:设置hash key对应的field的value
    • hget key field:删除hash key对应的field的value
  • hexists hlen
    • hexists key field:判断hash key是否有field
    • hlen key:获取hash key field的数量
  • hmget hmset
    • hmget key field1 field2 ... fieldN:批量获取hash key的一批field对应的值
    • hmset key field1 value1 field2 value2 ... fieldN valueN:批量设置hash key的一批field对应的值
  • hgetall hvals hkeys
    • hgetall key:返回hash key对应所有的field和value
    • hvals key:返回hash key对应所有的field的value
    • hkeys key:返回hash key对应所有的field

2. 快速实战

  • 实现如下功能:记录网站每个用户个人主页的访问量
    • hincrby user:1:info pageview count
  • 实现如下功能:缓存视频的基本信息(数据源在MySQL中)伪代码
public VideoInfo get(long id) {
    String redisKey = redisPrefix + id;
    Map<String, String> hashMap = redis.hgetAll(redisKey)
    VideoInfo viderInfo = transferMapToVideo(hashMap);
    if (videoInfo == null) {
        videoInfo = mysql.get(id);
        if (videoInfo != null) {
            redis.hmset(redisKey, transferVideoToMap(videoInfo));
        }
    }
    return videoInfo;
}

3. 查漏补缺

  • string vs hash
    • 相似的API
string hash
get hget
set setnx hset hsetnx
del hdel
incr incyby decr decrby hincrby
mset hmset
mget hmget
  • 如何更新用户属性
  • string实现
    • key : user:1
    • value : (serializable:json,xml,protobuf) {"id":1, "name":"ronaldo", "age":40, "pageView":5000000}
  • string实现
    • key : user:1:name user:1:age user:1:pageView
    • value : world 40 500000
  • hash实现
    • key : hset user:1:info age 41
    • value :
命令 优点 缺点
string v1 编程简单,可能节约内存 序列化开销,设置属性要错做整个数据
string v2 直观,可以部分更新 内存占用较大,key较为分散
hash 直观,节省空间,可以部分更新 编程稍微复杂,ttl不好控制
  • hsetnx hincrby hincrbyfloat
    • hsetnx key field value:设置hash key对应的field的value(如果field已经存在,则失败)
    • hincrby key field intCounter:hash key对应的field的value自增intCounter
    • hincrbyfloat key field floatCounter:hincrby浮点数版
命令 时间复杂度
hget hset hdel O(1)
hexists O(1)
hincrby O(1)
hgetall hvals hkeys O(1)
hmget hmset O(n)

6. 列表

1. 结构和命令

  • 特点
    • 有序
    • 可以重复
    • 左右两边插入弹出
  • 链表的API都是以L开头
  • rpush
    • rpush key value1 value2 ... valueN:从列表右端插入值(1-N个)
  • lpush
    • lpush key value1 value2 ... valueN:从列表左端插入值(1-N个)
  • linsert
    • linsert key before|agter value newValue:在list指定的值前|后插入newValue
  • lpop
    • lpop key:从列表左侧弹出一个item
  • rpop
    • rpop key:从列表右侧弹出一个item
  • lrem
    • lrem key count value:根据count值,从列表删除所有value相等的项
      • count>0,从左到右,删除最多count个value相等的项
      • count<0,从右到左,删除最多Math.abs(count)个value相等的项
      • count=0,删除所有value的项
  • ltrim
    • ltrim key start end:按照索引范围修剪列表
  • lrange
    • lrange key start end(包含end):获取列表指定索引范围所有item
  • lindex
    • lindex key index:获取列表指定索引的item
  • llen
    • llen key:获取列表长度
  • lset
    • lset key index newValue:设置列表指定索引值为newValue

2. 快速实战

  • 微博 - TimeLine
    • 你关注的人更新微博,LPUSH

3. 查漏补缺

  • blpop brpop
    • blpop key timeout:lpop阻塞版本,timeout是阻塞超时时间,timeout=0为永远不阻塞
    • brpop key timeout:rpop阻塞版本,timeout是阻塞超时时间,timeout=0为永远不阻塞
  • TIPS
    • LPUSH + LPOP = Stack
    • LPUSH + RPOP = Queue
    • LPUSH + LTRIM = Capped Collection
    • LPUSH + BRPOP = Message Queue

7. 集合

1. 结构和命令

  • 特点
    • 无序
    • 无重复
    • 集合间操作
  • 所有的API都是以S开头
  • sadd srem
    • sadd key element:向集合key添加element(如果element已经存在,添加失败)
    • srem key element:将集合key中的element移除掉
  • scard sismember srandmember smembers
    • scard key:算一下集合中元素个数
    • sismember key element:判断元素是否在集合中
    • srandmember key count:从集合中随机取出count个元素
    • smembers key:取出元素中的所有元素
  • sdiff sinter sunion
    • sdiff key1 key2:集合差集
    • sinter key1 key2:集合交集
    • sunion key1 key2:集合并集
    • sdiff|sinter|sunion + store destkey:将差集,交集,并集结果保存在destkey中

2. 快速实战

  • 抽奖系统
  • Like,赞,踩
  • 标签(tag)
  • 共同关注

3. 查漏补缺

  • TIPS:
    • SADD = Tagging
    • SPOP/SRANDMEMBER = Random item
    • SADD + SINTER = Social Graph

8. 有序集合

1. 结构和命令

  • 所有的API都是以Z开头
  • zadd
    • zadd key score element(可以是多对):添加score和element
  • zrem
    • zrem key element(可以是多对):删除元素
  • zscore
    • zscore key element:返回元素的分数
  • zincrby
    • zincrby key increScore element:增加或减少元素的分数
  • zcard
    • zcard key:返回元素的总个数
  • zrange
    • zrange key start end [WITHSCORES]:返回指定索引范围内的升序元素[分值]
  • zrangebyscore
    • zrangebyscore key minScore maxScore [WITHSCORES]:返回指定分数范围内的升序元素[分值]
  • zcount
    • zcount key minScore maxScore:返回有序集合内在指定分数范围内的个数
  • zremrangebyrank
    • zremrangebyrank key start end:删除指定排名内的升序元素
  • zremrangebyscore
    • zremrangebyscore key minScore maxScore:删除指定分数内的升序元素

2. 快速实战

  • 排行榜

3. 查漏补缺

  • zrevrank
  • zrevrange
  • zvevrangebyscore
  • zinterstore
  • zunionstore

3. Redis客户端

1. Java客户端:Jedis

// 生成一个Jedis对象,这个对象负责和指定Redis节点进行通信。
Jedis jedis = new Jedis("127.0.0.1", 6379);
// jedis执行set操作
jedis.set("hello", "world");
// jedis执行get操作
String value = jedis.get("hello");
  • Jedis(String host, int port, int connectionTimeout, int soTimeout)
    • host:Redis节点所在机器的IP
    • port:Redis节点的端口
    • connectionTimeout:客户端连接超时
    • soTimeout:客户端读写超时
  • jedis直连
    • 生成jedis对象
    • jedis执行命令
    • 返回执行结果
    • 关闭jedis连接
  • jedis连接池
    • 从资源池借jedis对象
    • jedis执行命令
    • 返回执行结果
    • 归还jedis对象给连接池

4. Redis的一些其他功能

1. 慢查询

生命周期

  • 慢查询发生在第3阶段。
  • 客户端超时不一定慢查询,但慢查询是客户端超时的一个可能因素。

1. 两个配置 - slowlog-max-len:我们的慢查询队列长度是多少

慢查询队列

  • 先进先出队列
  • 固定长度
  • 保存在内存中

2. 两个配置 - slowlog-log-slower-than:当查询时间小于多少我们认为是慢查询

  • 慢查询阈值(单位:微秒)
  • slowlog-log-slower-than = 0,记录所有命令
  • slowlog-log-slower-than < 0,不记录任何命令

3. 配置方法

  • 默认值
    • config get slowlog-max-len = 128
    • config get slowlog-log-slower-than = 10000
  • 修改配置文件重启
  • 动态配置

4. 慢查询命令

  • slowlog get [n] : 获取慢查询队列
  • slowlog len : 获取慢查询队列长度
  • slowlog reset : 清空慢查询队列

5. 运维经验

  • slowlog-max-len不要设置过小,通常设置1000左右。
  • slowlog-log-slower-than不要设置过大,默认10ms,通常设置1ms。
  • 理解命令生命周期。
  • 定期持久化慢查询。

2. pipeline

  • 1次时间 = 1次网络时间 + 1次命令时间
  • n次时间 = n次网络时间 + n次命令时间

1. 什么是流水线

  • 将一批命令进行批量打包,在服务端进行批量计算,然后按顺序将结果返回给我们。
  • 1次pipeline(n条命令) = 1次网络时间 + n次命令时间
  • 两点注意
    • Redis的命令时间都是微秒级别。
    • pipeline每次条数要控制(网络)。

2. pipeline-Jedis实现

public void PipelineTest() {
    Jedis jedis = new Jedis("192.168.242.129", 6379);
    for (int i = 0; i < 100; i++) {
        Pipeline pipeline = jedis.pipelined();
        for (int j = i * 100; j < (i + 1) * 100; j++) {
            pipeline.hset("hashkey:" + j, "field" + j, "value" + j);
        }
        pipeline.syncAndReturnAll();
    }
}

3. 与原生M操作?

  • M操作是原子操作
  • Pipeline不是原子操作

4. 使用建议

  • 注意每次Pipeline携带数据量。
  • Pipeline每次只能作用在一个Redis节点上。
  • M操作与Pipeline的区别。

3. 发布订阅

1. 角色

  • 发布者(publisher)
  • 订阅者(subscriber)
  • 频道(channel)

发布模型

2. API

  • publish(发布命令)
    • publish channel message
  • subscribe(订阅)
    • subscribe [channel] #一个或多个
  • unsubscribe(取消订阅)
    • unsubscribe [channel] #一个或多个

3. 消息队列

  • 发布订阅特点是发布消息之后其他所有订阅者都可以获得消息。
  • 消息队列是一种抢的过程,发布者发布了一条消息只有一个订阅者获得。(redis并没有提供这种功能)

4. 发布订阅总结

  • 发布订阅模式中的角色
  • 重要的API

4. bitmap(位图)

127.0.0.1:6379> set hello big
OK
127.0.0.1:6379> getbit hello 0
(integer) 0
127.0.0.1:6379> getbit hello 1
(integer) 1
  • 对于redis,我可以直接操作位。

1. 相关API

  • setbit
    • setbit key offset value:给位图指定索引设置值
127.0.0.1:6379> setbit hello 7 1
(integer) 0
127.0.0.1:6379> get hello
"cig"
  • getbit
    • getbit key offset:获取指定偏移量的值
  • bitcount
    • bitcount key [start end]:获取位图指定范围(start到end,单位为字节,如果不指定就是获取全部)位值为1的个数
  • bitop
    • bitop op destkey key [key ...]:做多个Bitmap的and(交集),or(并集),not(非),xor(异或)操作并将结果保存在destkey中
  • bitpos
    • bitpos key targetBit [start] [end]:计算位图指定范围(start到end,单位为字节,如果不指定就是获取全部)第一个偏移量对应的值等于targetBit的位置

2. 独立用户统计

  • 使用set和bitmap
  • 1亿用户,每天5千万独立访问
数据类型 每个userid占用空间 需要存储的用户量 全部内存量
set 32位(整型) 50000000 200MB
bitmap 1位 100000000 12.5MB

3. 使用经验

  • type = string,最大512M
  • 注意setbit时的偏移量,可能有较大耗时
  • 位图不是绝对的好

5. HyperLogLog

1. 基于HyperLogLog算法:极小空间完成独立数量统计

  • 本质还是字符串

2. 三个命令

  • pfadd key element [element ...]:向hyperLogLog添加元素
  • pfcount key [key ...]:计算hyperloglog的独立总数
  • pfmerge destkey sourcekey [sourcekey ...]:合并多个hyperloglog

3. 使用经验

  • 是否能容忍错误?(错误率:0.81%)
  • 是否需要单条数据?

6. GEO

1. GEO(地理信息定位):存储经纬度,计算两地距离,范围计算等

2. 使用场景

  • 摇一摇
  • 周围餐馆,酒店

3. API

  • geoadd
    • geo key longitude latitude member [longitude latitude member ...]:增加地理位置信息
  • geopos
    • geopos key member [member ...]:获取地理位置信息
  • geodisk
    • geodisk key member1 member2 [unit]:获取两个地理位置的距离(unit:m(米 ...)
  • georadius

4. 相关说明

  • since 3.2+
  • type geoKey = zset

最后

大家可以关注我的微信公众号一起学习进步。