赞
踩
目录结构:
简单来说 Redis 就是一个使用 C 语言开发的数据库,不过与传统数据库不同的是 Redis 的数据是存在内存中的 ,也就是它是内存数据库,所以读写速度非常快,因此 Redis 被广泛应用于缓存方向。
常用命令
# 查看所有符合条件的key
keys [pattern]
keys h?llo
# 删除key
del key [key ...]
# 检查是否存在
exists key
# 给key设置超时时间
#秒级:
expire key 5
#毫秒级:
pexpire key 5
#查看key的剩余时间
ttl key
pttl key
# 返回存储类型
type key
最常规的set/get操作,value可以是String也可以是数字。一般做一些复杂的计数功能的缓存。
介绍
string 数据结构是简单的 key-value 类型。虽然 Redis 是用 C 语言写的,但是 Redis并没有使用 C 的字符串表示,而是自己构建了一种 简单动态字符串(simple dynamic string,SDS)。相比于 C 的原生字符串,Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串⻓度复杂度为 O(1)(C 字符串为 O(N)),除此之外,Redis 的SDS API 是安全的,不会造成缓冲区溢出。
常用命令:
set:
set key value [ex seconds] [px milliseconds] [nx|xx]
#设置键为Hello,值为World的键值对
set Hello World
# set命令有几个选项:
ex seconds:为键设置秒级过期时间。
px milliseconds:为键设置毫秒级过期时间。
nx:键必须不存在,才可以设置成功,用于添加。
xx:与nx相反,键必须存在,才可以设置成功,用于更新。
#批量设置
mset key1 value1 [key2 value2..... ]
get:
get key
#获取键为Hello的值
get Hello
#批量获取
mget key [key2....]
应用场景
字符串可以说是Redis应用最广泛的数据结构,我们来看一下在实际的开发中一些典型的应用场景。
缓存
计数
许多应用都会使用Redis作为计数的基础工具,它可以实现快速计数、 查询缓存的功能,同时数据可以异步落地到其他数据源。例如记录文章的阅读次数。
共享Session
分布式锁
利用setnx命令可以实现分布式锁,由于Redis的单线程命令处理机制,如果有多个客户端同时执行setnx key value, 根据setnx的特性只有一个客户端能设置成功,所以setnx可以作为分布式锁的一种实现方案。
Redis 的字典和 Java 语言里面的 HashMap类似。在Redis中,哈希类型是指键值本身又是一个键值对结构,形如value={{field1,value1},…{fieldN,valueN}}
常用命令
hset:
hset key field value
#key为 user1的一组hash
hset user1 name wz
hget:
hget key field
hget user1 naem
hdel
#删除 field
hdel key field[field...]
hdel user1 name
应用场景:
存储对象
hash的filed-value的结构非常适合用来存储对象,field用来存储属性名称,value用来存储属性值。在一些客户端里也提供了json序列化器。
Redis 的列表类似于 Java 语言里面的 LinkedList,同样地list 的插入和删除操作非常快,时间复杂度为 O(1),但是索引定位很慢,时间复杂度为O(n)。
常用操作:
操作类型 | 操作 |
---|---|
添加 | rpush lpush linsert |
查找 | lrange lindex llen |
删除 | lpop rpop lren ltrim |
修改 | lset |
阻塞操作 | blpop brpop |
添加
#从右边插入
rpush key value[value....]
# 从左边插入
lpush key value[value.....]
#向某个元素(pivot)前或者后插入元素
linsert key before|after pivot value
查找
#查找指定范围内的元素
lrange key start end
#获取指定下标的元素
lindex key index
#列表长度
llen key
删除
#从左侧弹出元素
lpop key
#从右侧弹出元素
rpop key
#删除指定元素
lrem key count value
#修改
lset key index newValue
#阻塞形式弹出
blpop key[key....] timeout
brpop key[key....] timeout
应用场景:
消息队列
list可以灵活组合,在不同的场景使用,总结如下:
集合类似Java语言中的HashSet,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。
常用操作:
添加
sadd key element[element...]
删除
srem key element[element....]
从集合中随机弹出
spop key
集合的操作
#交集
sinter key[key....]
#并集
suinon key [key...]
#差集
sdiff key [key...]
应用场景:
标签
集合类型比较典型的使用场景是标签(tag)。例如一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻比较感兴趣,这些兴趣点就是标签。有了这些数据就可以得到喜欢同一个标签的人,以及用户的共 同喜好的标签,这些数据对于用户体验以及增强用户黏度比较重要。
共同关注
zset 可能是 Redis 提供的最为特色的数据结构,它类似于 Java 的 SortedSet 和 HashMap 的结合体,一方面它是一个 set,保证了内部value 的唯一性,另一方面它可以给每个 value 赋予一个 score,代表这个 value 的排序权重。
相比于set增加了一个权重score,底层使用跳表
常用命令:
增
zadd key score member [score member...]
zadd user:rank 5 吴老狗
计算
#计算排名
zrank key member
#计算成员个数
zcard key member
#删除成员
zrem key member
应用场景:
可以用于统计博客、视频网站等作品的点赞数,可以根据点赞数对作品进行排行。
持久化就是把内存的数据写到磁盘中,防止服务宕机导致内存数据丢失。
将某一个时刻的内存数据,以二进制的方式写入磁盘。
RDB
是 Redis 默认的持久化方案。RDB持久化时会将内存中的数据写入到磁盘中,在指定目录下生成一个dump.rdb
文件。Redis 重启会加载dump.rdb
文件恢复数据。
bgsave
是主流的触发 RDB 持久化的方式,执行过程如下:
Redis启动时会读取RDB快照文件,将数据从硬盘载入内存。通过 RDB 方式的持久化,一旦Redis异常退出,就会丢失最近一次持久化以后更改的数据。
触发持久化的方式:
手动触发: 用户执行save
或bgsave
命令。save命令会阻塞所有客户端的请求,bgsave异步进行快照操作。
被动触发:
根据规则进行自动快照, bgsave 100 10(100秒内至少10个键被修改则触发快照)
从节点进行全量复制操作,主节点会自动执行bgsave
生成RDB文件发给从节点。
默认情况下执行shutdown
命令时,如果没有开启AOF持久化功能,自动执行bgsvae
优缺点:
BGSAVE
每次运行都要执行fork
操作创建子进程,属于重量级操作,频繁执行成本比较高。类似于MySQL的binlog
AOF(append only file)持久化:以独立日志的方式记录每次写命令,Redis重启时会重新执行AOF文件中的命令达到恢复数据的目的。AOF的主要作用是解决了数据持久化的实时性,AOF 是Redis持久化的主流方式。
默认情况下Redis没有开启AOF方式的持久化,可以通过appendonly
参数启用:appendonly yes
。开启AOF方式持久化后每执行一条写命令,Redis就会将该命令写进aof_buf
缓冲区,AOF缓冲区根据对应的策略向硬盘做同步操作。
默认情况下系统每30秒会执行一次同步操作。为了防止缓冲区数据丢失,可以在Redis写入AOF文件后主动要求系统将缓冲区数据同步到硬盘上。可以通过appendfsync
参数设置同步的时机。
appendfsync always //每次写入aof文件都会执行同步,最安全最慢,不建议配置
appendfsync everysec //既保证性能也保证安全,建议配置
appendfsync no //由操作系统决定何时进行同步操作
注意事项:
优缺点:
优点:
fsync
操作,如果Redis进程挂掉,最多丢失1秒的数据。append-only
的模式写入,所以没有磁盘寻址的开销,写入性能非常高。缺点:
在 Redis 4.0 后,增加了 AOF 和 RDB 混合的数据持久化机制: 把数据以 RDB 的方式写入文件,再将后续的操作命令以 AOF 的格式存入文件,既保证了 Redis 重启速度,又降低数据丢失风险。
主要是由于Redis在写入日志之前,不对命令进行语法检查,所以只记录执行成功的命令,避免出现记录错误命令的情况,而且在命令执行后再写日志不会阻塞当前的写操作。
那写后日志有什么风险呢?
常用的分布式缓存Redis单机并发量能达到万级,常用的关系型数据库MySQL一般并发量是千级,他们支持的并发量可能差十倍,所以要尽可能把流量拦截在缓存层。
一个并发访问量比较大的key在某个时间过期,导致所有的请求直接打在DB上。
解决方案:
缓存穿透指的查询缓存和数据库中都不存在的数据,这样每次请求直接打到数据库,就好像缓存不存在一样。
解决方案:
缓存空值/默认值
使用布隆过滤器
布隆过滤器里会保存数据是否存在,如果判断数据不不能再,就不会访问存储。
指大量缓存数据在同一时刻失效,使这些访问都直接访问数据库,使数据库崩溃。
解决方案:
提高缓存可用性
过期时间
熔断降级
缓存和数据库的强一致性无法实现!
如果采用先更新数据库,再删除缓存的方式,我们更新数据库成功,接下来还没来删除缓存,或者删除缓存失败怎么办?
延迟双删
先删除缓存,再更新数据库,休眠一定时间,再删除缓存。
异步更新缓存(基于订阅binlog的同步机制)
读redis当中的数据,mysql负责增删改操作,然后利用消息队列异步更新Redis当中的缓存。
一旦mysql当中的进行了增删改的操作之后,就会记录到binlog当中,就可以把binlog相关的消息推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。
Redis的单线程指的是执行命令时的单线程
通常来讲,单线程处理能力要比多线程差,那么为什么Redis使用单线程模型会达到每秒万级别的处理能力呢?
IO多路复用:
引用知乎上一个高赞的回答来解释什么是I/O多路复用。假设你是一个老师,让30个学生解答一道题目,然后检查学生做的是否正确,你有下面几个选择:
第一种就是阻塞IO模型,第三种就是I/O复用模型,Linux下的select、poll和epoll就是干这个的。将用户socket对应的fd注册进epoll,然后epoll帮你监听哪些socket上有消息到达,这样就避免了大量的无用操作。此时的socket应该采用非阻塞模式
。
单线程也会有一个问题
:对于每个命令的执行时间是有要求的。如果某个命令执行过长,会造成其他命令的阻塞,对于Redis这种高性能的服务来说是致命的,所以Redis是面向快速执行场景的数据库。
Redis6.0引入了多线程的特性,这个多线程是在哪里呢?——「是对处理网络请求过程采用了多线程」。
将从前的一台 Redis 服务器,同步数据到多台从 Redis 服务器上,即一主多从的模式,这个跟MySQL主从复制的原理一样。
使用 Redis 主从服务的时候,会有一个问题,就是当 Redis 的主从服务器出现故障宕机时,需要手动进行恢复,为了解决这个问题,Redis 增加了哨兵模式(因为哨兵模式做到了可以监控主从服务器,并且提供自动容灾恢复的功能)。
Redis Cluster 是一种分布式去中心化的运行模式,是在 Redis 3.0 版本中推出的 Redis 集群方案,它将数据分布在不同的服务器上,以此来降低系统对单主节点的依赖,从而提高 Redis 服务的读写性能。
❓为什么需要使用集群?哨兵还差点什么呢?
哨兵模式归根节点还是主从模式,在主从模式下我们可以通过增加salve节点来扩展读并发能力,但是没办法扩展写能力和存储能力,存储能力只能是master节点能够承载的上限。所以为了扩展写能力和存储能力,我们就需要引入集群模式。
集群中那么多Master节点,redis cluster在存储的时候如何确定选择哪个节点呢?
Redis Cluster采用的是类一致性哈希算法实现节点选择的。
Redis 事务的原理是将一个事务范围内的若干命令发送给Redis。然后再让Redis依次执行这些命令。
使用 MULTI
开启一个事务
在开启事务的时候,每次操作的命令将会呗插入到一个队列中,同时这个命令并不会被真正的执行;
EXEC
命令进行提交事务
一个事务范围内某个命令出错不会影响其他命令的执行,不保证原子性
discard:取消事务
watch:监视
使用Lua脚本可以保证事务的原子性。
Redis 通过 LUA 脚本创建具有原子性的命令:当lua脚本命令正在运行的时候,不会有其他脚本或 Redis 命令被执行,实现组合命令的原子操作。
在Redis中执行Lua脚本有两种方法:eval
和evalsha
。eval
命令使用内置的 Lua 解释器,对 Lua 脚本进行求值。
# 第一个参数是lua脚本,第二个参数是键名参数个数,剩下的是键名参数和附加参数
> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"
lua脚本作用
1、Lua脚本在Redis中是原子执行的,执行过程中间不会插入其他命令。
2、Lua脚本可以将多条命令一次性打包,有效地减少网络开销。
总结起来主要就是 3 个原因:
布式锁需要满足谁申请谁释放原则,不能释放别人的锁,也就是说,分布式锁,是要有归属的。
set lock key nx ex 3;
检查是否存在,不存在才能加锁并且设置超时时间
问题:执行完毕后,检查锁,再释放,这些操作不是原子化的。可能锁获取时还是自己的,删除时却已经是别人的了。这可怎么办呢?
有了Lua的特性,Redis才真正在分布式锁、秒杀等场景,有了用武之地,下面便是改造之后的流程:
前面我们谈及的内容,基本是基于单机考虑的,如果Redis挂掉了,那锁就不能获取了。这个问题该如何解决呢?
主从容灾
最简单的一种方式,就是为Redis配置从节点,当主节点挂了,用从节点顶包。(但是主从切换需要人工参与,可以使用哨兵模式灵活自动切换。)
这种方式由于同步有延迟,可能导致丢掉一部分数据,分布式锁可能失效。
多机部署(假设集群中有五个主节点)
当Redis的内存超过最大允许的内存之后,Redis 会触发内存淘汰策略,删除一些不常用的数据,以保证Redis服务器正常运行。
Redisv4.0前提供 6 种数据淘汰策略:
Least Recently Used
),最近使用。利用LRU算法移除设置了过期时间的keyRedisv4.0后增加以下两种:
内存淘汰策略可以通过配置文件来修改,相应的配置项是maxmemory-policy
,默认配置是noeviction
。
本文参考:
超过最大允许的内存之后,Redis 会触发内存淘汰策略,删除一些不常用的数据,以保证Redis服务器正常运行。
Redisv4.0前提供 6 种数据淘汰策略:
Least Recently Used
),最近使用。利用LRU算法移除设置了过期时间的keyRedisv4.0后增加以下两种:
内存淘汰策略可以通过配置文件来修改,相应的配置项是maxmemory-policy
,默认配置是noeviction
。
本文参考:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。