Redis大全手册(上)

API的理解和使用

通用命令

keys
dbsize #计算key的总数
exists key  #检查key是否存在
del key [key]   #删除指定key-value
type key    #返回key的类型
expire key seconds      #key在seconds过期
ttl key     #查看key剩余的过期时间
persist key     #去掉key的过期时间
复制代码

-2表示过期
-1代表key存在,并且没有过期时间

kyes基本不在生产环境使用

keys *    #遍历所有key
key [pattern]

复制代码
命令时间复杂度
keysO(n)
dbsizeO(1)
delO(1)
existsO(1)
expireO(1)
typeO(1)

数据结构和内部编码

redis为什么这么快?

  1. 纯内存
  2. 非阻塞IO
  3. 避免线程切换和竞态消耗

单线程需要注意什么?

  1. 一次只运行一条命令
  2. 拒绝长(慢)命令 keys, flushall, flushdb, slow lua script, mutil/exec, operate big value(collention)
  3. 其实不是单线程
    fysnc file descriptor
    close file descriptor
    复制代码

字符串

get key     #获取key对应的value O(1)
set key value   #设置key-value  O(1)
del key     #删除key-value  O(1)
mset key value key value    #批量设置key-value   O(n)
mget key key   #批量获取key-value    O(n)

incr key    #key自增1,如果key不存在,自增后get(key)=1   O(1)
decr key    #key自减1,如果key不存在,自减后get(key)=-1   O(1)
incrby key k    #key自增k,如果key不存在,自增后get(key)=k   O(1)
decrby key k    #key自减k,如果key不存在,自减后get(key)=-k   O(1)

set key value   #不管key是否存在,都设置   O(1)
setnx key value  #key不存在,才设置     O(1)
set key value xx    #key存在,才设置     O(1)

getset key newvalue     #set key newvalue并返回旧的value
append key value    #将value追加到旧的value
strlen key  #返回字符串的长度(注意中文)

incrbyfloat key 3.5   #增加key对应的值3.5
getrange key start end   #获取字符串指定下标的所有的值
setrange key index value    #设置下标所有对应的值
复制代码

记录网站每个用户个人主页的访问量

#redis实现
incr userid:pagevies(单线程,无竞争)
hincrby user:1:info pageview count

#java模拟代码
public VideoInfo get(long id){
    String redisKey = redisPrefix + id;
    Map<String,String> hashMap = redis.hgetAll(redisKey);
    VideoInfo videoInfo = transferMapToVideo(hashMap);
    if(videoInfo == null){
        videoInfo = mysql.get(id);
        if(videoInfo != null){
            redis.hmset(redisKey, transferMapToVideo(videoInfo))
        }
    }
    return videoInfo;
}
复制代码

hash

哈希键值结构

hmset key field value field value   #批量设置   O(n)
hmget key field field   #批量获取       O(n)
hget key field   #获取hash key对应的field的value    O(1)
hset key field value    #设置hash key对应field的value   O(1)
hdel key field  #删除hash key对应的field的value     O(1)
hexists key field   #判断hash key 是否有field   O(1)
hlen key    #获取hash key field的数量       O(1)
hgetall key     #h返回hash key对应所有的field和value    O(n)
hvals key   #返回hash key对应所有field的value   O(n)
hkeys key   #返回hash key对应所有field      O(n)
hsetnx key field value  #设置hash key对应field的value(如field存在,则失败)    O(1)
hincrby key field intCounter    #hash key 对应的field的value自增intCounter  O(1)
hincrbyfloat key field floatCounter     #hincrby浮点数版    O(1)
复制代码

小心使用hgetall(redis单线程) 例子:如保存一个用户的信息的实现,下面说3种情形,当然还有更多种其他方式

  • String v1
  • String v2
  • hash
    比较
命令优点缺点
string v1编程简单,可能节约内存1. 序列号开销
2. 设置属性要操作整个数据
string v2直观,可以部分更新1. 内存占用较大
2. key较为分散
hash直观、节省空间、可以部分更新1. 编程稍微复杂
2. ttl不好控制

list

特点:有序、可以重复、左右两边插入弹出

rpush key value value ...valueN     #从列表右端插入值(1-N个)
lpush key value value ...valueN     #从列表左端插入值(1-N个)
linsert key before|after value newValue     #在list指定的前|后插入newValue
lpop key    #从列表左侧弹出一个item
rpop key    #从列表右侧弹出一个item

#根据count值,从列表中删除所有value相等的项
#count > 0,从左到右,删除最多count个value相等的项
#count < 0,从右到左,删除最多Math.abs(count)个value相等的项
#count = 0,删除所有value相等的项
lrem key count value

ltrim key start end     #按照索引范围修剪列表      O(n)
lrange key start end    #获取列表指定索引范围所有item   O(n)
llen key    #获取列表长度   O(1)
lset key index newValue     #设置列表指定索引值为newValue   O(n)
blpop key timeout   #lpop阻塞版本,timeout是阻塞超时时间,timeout=0为永远不阻塞     O(1)
brpop key timeout   #rpop阻塞版本,timeout是阻塞超时时间,timeout=0为永远不阻塞     O(1)

##小建议-数据结构类比
lpush + lpop = stack
lpush + rpop = queue
lpush + ltrim = capped collection
lpush + brpop = message quere
复制代码

慢查询

  • slowlog-max-len
    1. 先进先出队列
    2. 固定长度
    3. 保存在内存中

慢查询命令

slowlog get [n]     #获取慢查询队列
slowlog len     #获取慢查询队列长度
slowlog reset   #清空慢查询队列
复制代码
  • 慢查询阀值(单位:微妙)
  • slowlog-log-slower-than=0 记录所有命令
  • slowlog-log-slower=than<0 不记录任何命令 配置方式
1. 默认值
config get slowlog-max-len = 128
config get slowlog-log-slower-than = 1000
2. 修改配置文件重启
3. 动态配置
config set slowlog-max-len 1000
config set slowlog-log-slower-than 1000
复制代码

运维经验

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

pipeline

批量网络命令通信模型

什么是流水线
流水线作用

命令N个命令操作1次pipeline(n个命令)
时间n次网络 + n次命令1次网络 + n次命令
数据量1条命令n条命令
  • redis的命令时间是微秒级别
  • pipeline每次条数要控制(网络原因)

从上图举例,redis命令的执行时间是很快的,但是由于数据需要通过网络传输,由于2个地区相隔很远,数据以光速度传播也需要时间,然而这个时间有可能比redis执行时间要长。

pipeline-Jedis实现

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
    <type>jar</type>
</dependency>

#没用pipeline, 1W次hset需要50s
Jedis jedis = new Jedis("127.0.0.1", 6379);
for(int i=0;i<10000;i++){
    jedis.hset("hashkey:"+i,"field"+i, "value"+i);
}

#使用pipeline
Jedis jedis = new Jedis("127.0.0.1", 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();
}
复制代码

使用建议

  1. 注意每次pipeline携带的数据量
  2. pipeline每次只能作用在一个Redis节点上
  3. M操作和pipeline的区别

发布订阅

角色 发布者(publisher) 订阅者(subscriber) 频道(channel) 模型

publish channel message     #发布消息
subscribe [channel]     #一个或多个
unsubscribe [channel]     #一个或多个
psubscribe [pattern...]     #订阅模式
punsubscribe [pattern...]   #退订指定的模式
pubsub channels     #列出至少有一个订阅者的频道
pubsub numsub [channel...]      #列出给定频道的订阅者数量
pubsub numpat    #列出被订阅模式的数量
复制代码

位图

setbit key offset value     #给位图指定索引设置值
getbit key offset       #获取位图指定索引的值
bitcount key [start end]    #获取位图指定范围(start到end,单位为字节,如果不指定就是获取全部)位值为1的个数
bitop key targetBit [start] [end]   #计算位图指定范围第一个偏移量对应的值等于targetBit的位置
复制代码

独立用户统计

  1. 使用set和Bitmap两种方式
  2. 1亿用户,5千万独立
数据类型每个userid占用空间需要存储的用户量全部存储量
set32位(假设userid用的是整型,实际场景很多用长整型)5000000032位*50000000=190.7348633MB
Bitmap1位1000000001位*100000000=11.920929MB
一天一个月一年
set200M6G72G大约值
Bitmap12.5M375M4.5G大约值

只有十万独立用户呢?

数据类型每个userid占用空间需要存储的用户量全部存储量
set32位(假设userid用的是整型,实际场景很多用长整型)10000032位*1000000=0.3814697MB
Bitmap1位1000001位*100000000=0.0119209MB

使用建议

  1. type = string,最大512MB
  2. 注意setbit的偏移量,可能有较大耗时
  3. 位图不是绝对好

HyperLogLog

  1. 极小空间完成独立数量统计
  2. 本质还是字符串
  3. pfcount 统计有一定错误率0.81%
  4. 无法取出单条数据
pfadd key element [element...]      #向hyperloglog添加元素
pfcount key [key]       #计算hyperloglog的独立总数
pfmerge destkey sourcekey [sourcekey]   #合并多个hyperloglog
复制代码

geo地理信息定位

  1. 3.2版本以后才有geo
  2. geoKey的类型是zset,type geoKey = zset
  3. 没有删除的API,可以使用zrem key member
geo key longitude latitude member [longitude latitude member...]    #增加地理位置信息
geopos key member [member...]       #获取地理位置信息
geodist key member1 member2 [unit]    #获取两个地理位置的距离,unit:m、km、mi、ft
复制代码

更多命令参考

Redis持久化的取舍和选择

redis持久化RDB

触发机制
save 阻塞的 文件策略:如果存在老的RDB文件,替换 时间复杂度O(n)

bgsave

save与bgsave

命令savebgsave
IO类型同步异步
阻塞是(阻塞发生再fork)
复杂度O(n)O(n)
优点不会消耗额外内存不阻塞客户端命令
缺点阻塞客户端命令需要fork,消耗内存

# 配置redis.conf
 save 900 1
 save 300 10
 save 60 10000
 dbfilename dump.rdb
 dir ./
 stop-write-on-bgsave-error yes
 rdbcompression yes
 rdbchecksum yes
 
#最佳配置
 dbfilename dump-${port}.rdb        #指定对应哪个redis的备份
 dir /bigdiskpath       #指定具体文件目录
 stop-write-on-bgsave-error yes
 rdbcompression yes
复制代码

触发机制

  1. 全量复制
  2. debug reload
  3. shutdown

RDB总结

  1. RDB是redis内存到硬盘的快照,用于持久化
  2. save通常会阻塞redis
  3. bgsave不会阻塞redis,但是会fork新进程
  4. save自动配置满足任一就会被执行
  5. 有些触发机制不容忽视

AOF

RDB有什么问题 耗时、耗性能 不可控、丢失数据

AOF运行原理-创建

AOF运行原理-恢复

AOF的三种策略 always

everysec
no

命令alwayseverysecno
优点不丢失数据每秒一次fsync丢一秒数据不用管
缺点IO开销较大,一般的sata盘只有几百TPS丢一秒数据不可控

AOF重写

  • 减少硬盘占用量
  • 加速回复速度

AOF重写2种方式

  • bgrewriteaof命令

  • 自动

    配置名含义
    auto-aof-rewrite-min-sizeAOF文件重写需要的尺寸
    auto-aof-rewrite-percentageAOF文件增长率
    统计名含义
    aof_current_sizeAOF当前尺寸(单位:字节)
    aof_base_sizeAOF上次启动和重写的尺寸(单位:字节)

    自动触发实际(同时满足)

    • aof_current_size > auto-aof-rewrite-min-size
    • aof_current_size - aof_base_size/aof_base_size > auto-aof-rewrite-percentage
    #配置redis.conf
    appendonly yes
    appendfilename "appendonly-${port}.aof"
    appendfsync everysec
    dir /bigdiskpath
    no-appendfsync-on-rewrite yes
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb
    复制代码

AOF重写流程

AOF阻塞问题

大于2秒会造成主线程阻塞,无法进行后续客户端发来的命令 每秒刷盘的策略不止是只丢失1秒的数据,也有可能是几秒

如何定位

  • Redis日志
  • reids命令info Persistence(无法看到具体时间点)
  • linux top 命令观察IO使用率

RDB和AOF选择

命令RDBAOF
启动优先级
体积
恢复速度
数据安全性丢数据根据策略决定
轻重

最佳策略

  • 小分片
  • 缓存或存储
  • 监控(硬盘、内存、负载、网络)
  • 足够的内存

fork操作

  1. 同步操作(阻塞)
  2. 与内存量息息相关:内存越大,耗时越长(与机器类型无关)
  3. info:latest_fork_usec

改善fork

  1. 优先使用物理机或者高效支持fork操作的虚拟化技术
  2. 控制redis实例最大可用内存:maxmemory
  3. 合理配置Linux内存分配策略:vm.overommit_memory=1
  4. 降低fork频率:例如放宽AOF重写自动触发机制,不必要的全量复制

子进程开销与优化

  1. CPU:
    • 开销:RDB和AOF文件生成,属于CPU密集型
    • 优化:不做主reids CPU绑定,不和密集型CPU部署在一起
  2. 内存
    • 开销:fork内存开销,Linux:copy-on-write
    • 优化:Linux:echo never > /sys/kernel/mm/transparent_hugepage/enabled(关闭增加fork速度)
  3. 硬盘
    • 开销:RDB和AOF文件写入,可以结合iostat,iotop分析
    • 优化:
      1. 不和高硬盘负载服务部署再一起:存储服务,消息队列等。
      2. no-appendfsync-on-rewrite = yes
      3. 根据写入量决定磁盘类型:例如SSD
      4. 单机多实例持久化文件目录可以考虑分盘存储

redis复制的原理与优化

单机有什么问题? 机器故障 容量瓶颈 QPS瓶颈

简单总结

  1. 一个master可以有多个slave
  2. 一个slave只能有一个master
  3. 数据流向是单向的,master到slave

redis配置参数说明 Redis主从复制和集群配置

slaveof ip port
slave-read-only yes
slaveof on one
复制代码

全量复制

开销:

  1. bgsave时间
  2. RDB文件网络传输时间
  3. 从节点清空数据时间
  4. 从节点加载RDB的时间
  5. 可能的AOF重写时间

部分复制

Redis主从复制和集群配置

主从复制的常见问题 读写分离

  1. 读流量分摊到从节点,提高访问速度
  2. 可能遇到问题:复制数据延迟、读到过期数据、从节点故障

配置不一致

  1. 例如maxmemory不一致,丢失数据
  2. 例如数据结构优化参数(例如hash-max-ziplist-entries):内存不一致

规避全量复制

  1. 第一次全量复制
    • 第一次不可避免,从节点必须全量
    • 解决:小主节点(maxmemory)分数据量,访问低峰时刻
  2. 节点运行ID不匹配
    • 主节点重启(运行ID改变)
    • 解决:故障转移,例如哨兵或集群
  3. 复制积压缓冲区不足
    • 网络中断,部分复制无法满足
    • 解决:增大复制缓冲区配置rel_backlog_size,网络增强

规避复制风暴

  1. 单主节点复制风暴:
    • 问题:主节点重启,多从节点复制
    • 解决:更换复制拓扑
      slave-1从master复制数据之后,接下来的slave都从slave-1复制数据,减轻master压力
  2. 单机器复制风暴
    • 如图:机器宕机后,大量全量复制
    • 主节点分散多机器

Redis Sentinel

主从复制-master宕掉故障处理

Redis Cluster

缓存设计与优化

Redis云平台CacheCloud

阿里云Redis开发规范

内存管理

Redis 数据结构与内存管理策略(上)
Redis 数据结构与内存管理策略(下)
原理、方法双管齐下,大神带你细解Redis内存管理和优化

开发运维常见坑

redis调整内核参数
Redis安全
redis 热点Key的发现与解决之道
redis4.0之基于LFU的热点key发现机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值