redis 理解
程序员文章站
2022-03-05 10:06:02
...
Redis
redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库.
linux启动redis
./redis-server …/redis.conf
单机
数据类型
- 字符串(Strings) 字符串是动态字符串,是一个带长度的数组,会存在扩容,当数据超过1M,扩容时一次只会多扩1M的空间,需要注意的是字符串最大长度为512M
- 字符串列表(lists) 分为ziplist 和 quicklist
- 字符串集合(sets) 会自动去重
- 有序字符串集合(sorted sets)
- 哈希(hashes) hash是一个键值对集合,类似java中的map,特别适合存储对象
linux 客户端操作
string 存:set key value 取: get key
list 取: lrange key 0 -1 存: rpush key value
set 存: sadd key value(可以是数组) 取:smembers key
hash 存: hmset key value(键值对对象) 取:hget key value(可以是键值对对象中的key)
各种数据类型可以做什么
- String 遇到数值操作时,redis会将字符串类型转换成数值,INCR等指令本身就具有原子操作的特性.利用这个特性可以实现统计数量.
- lists LPUSH左侧插入新元素,RPUSH右侧插入新元素,BLPOP 移出并获取列表的第一个元素,BRPOP移出并获取列表最后一个元素. 可以做简单的队列. 利用LRANGE可以方便实现分页功能.
- sets 添加元素、删除元素、取交集、取并集、取差集.交集可以实现共同好友功能.利用集合也可以实现标签功能.
- sorted sets 有序号的情况下可以做排序. 每个成员关联一个评分,这个评分是用来排序使用.
- hash 可以做购物车,存储多条数据. 也可以做对象存储,一般对象用哪个string+json,频繁变化属性的可以用hash
Redis扩容 渐进式Rehash
rehash 动作并不是一次性、集中式的完成,而是分多次、渐进式的完成.
rehash具体操作
- 为ht[1]分配空间,让字典同时持有ht[0]和ht[1]两个哈希表.
- 在字典中维持一个索引计数器变量rehashidx ,并将它的值设置为0,表示rehash工作正式开始.
- 在rehash进行期间,每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定的操作以外,还会顺带将ht[0]哈希表在rehashidx 索引上的所有键值对rehash到ht[1],当rehash工作完成之后,程序将rehashidx属性的值增一.
- 随着字典操作的不断执行,最终在某个时间点上,ht[0]的所有键值对都会被rehash至ht[1],这时程序将rehashidx 属性的值设为-1,表示rehash操作已完成.
redis 事务
MULTI 、 EXEC 、 DISCARD 和 WATCH 是 Redis 事务相关的命令。
- MULTI:开启事务,redis将会后续的命令逐个放入队列中,然后使用EXEC命令来原子化执行这个命令系列.
- EXEC:执行事务中的所有操作命令.
- DISCARD: 取消事务,放弃执行事务块中的所有命令.
- WATCH: 监视一个或多个key,如果事务在执行前,这个key(或多个key)被其他命令修改,则事务被中断,不会执行事务中的任何命令.
- UNWATCH: 取消WATCH对所有key的监视.
什么是多路复用
多个客户端的链接复用一个或多个线程。
多路复用的原理
通过一种机制,一个进程能同时等待多个文件描述符,而这些文件描述符其中的任意一个进入读就绪(readable)状态,select()函数就返回。
redis 持久化
redis提供了两种持久化方式:RDB(Redis DataBase 和 AOF(Append Only File))
RDB 简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘介质上. 优缺点:优点是如果对数据完整性不是非常敏感,那RDB方式比AOF方式更加高效. 缺点:即使每五分钟都持久化一次,当redis故障时,仍然会有近五分钟的数据丢失.
AOF,则是换了一个角度来实现持久化,那就是将redis执行过的所有写指令记录下来,在下次redis重启时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了.
其实RDB和AOF可以同时使用,redis优先使用AOF方式来进行数据恢复.优缺点: 优点是每秒钟把缓存中的指令写到磁盘中.即使断电,磁盘满等问题都不会影响AOF文件的可用性.优点二,不小心执行了flushAll导致redis内存中的数据全部被清空了,只要redis配置了AOF持久化方式,且AOF文件没有被重写(rewrite),我们可以用最快的方式暂停redis并编辑AOF文件,将最后一行的FLUSHALL命令删除,然后重启redis就可以恢复redis的所有数据到FLUSHALL之前的状态了.
如果没有数据持久化的需求,也完全可以关闭RDB和AOF方式,这样的话,redis将变成一个纯内存数据库,想memcache一样.
redis内存回收机制
lru 最近最少使用的过期策略
lfu 最不经常使用 多次使用递加,长时间不使用递减
过期策略
- 立即过期 主动
- 惰性过期 被动
- 定时过期 最合理
集群
主从
- 方法一: redis.conf 中添加slaveof 192.168.142.210 6379
- 方法二: redis-cli -h 192.168.10.134 -p 6379 进入redis,输入 slaveof 192.168.10.135 6379
默认情况下,主从复制机器,从服务器是只读模式,使用一主多从可以实现高可用(不推荐).
哨兵(sentinel)模式(推荐)
哨兵模式是独立的进程,他会独立运行.
- 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
- 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
哨兵启动命令
redis-sentinel sentinel.conf
redis cluster集群条件(推荐)
redis cluster 是 redis 3.0版本正式推出,遇到单机内存,并发,流量等瓶颈时,可以采用cluster架构达到负载均衡目的. 六个redis服务,三主三从.
具有自动选举,主从切换的功能,也就是说,cluster具有sentinel的功能.
Redis Cluster采用哈希分区规则中的虚拟槽分区。虚拟槽分区巧妙地使用了哈希空间,使用分散度良好的哈希函数把所有的数据映射到一个固定范围内的整数集合,整数定义为槽(slot)。Redis Cluster槽的范围是0 ~ 16383。计算公式:slot = CRC16(key)&16383。
利用红黑树实现hash环
cluster redis启动命令
./redis-trib.rb create --replicas 1 192.168.10.134:6379 192.168.10.134:6380 192.168.10.135:6379 192.168.10.135:6380 192.168.10.136:6379 192.168.10.136:6380
重启注意事项
需要删除.rdb 和.aof的持久化文件,重启后不能保留原来数据
发布/订阅
当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher)。
而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE命令接收信息的时候,我们称这个客户端为订阅者(subscriber)。
为了解耦发布者(publisher)和订阅者(subscriber)之间的关系,Redis 使用了 channel (频道)作为两者的中介 —— 发布者将信息直接发布给 channel ,而 channel 负责将信息发送给适当的订阅者,发布者和订阅者之间没有相互关系,也不知道对方的存在。
redis实战
常用的客户端
redisTemplate 用的是
JedisConnectionFactory
LettuceConnectionFactory
哨兵和集群模式怎么拿到需要连接的redis的数据
- 方式一: 重定向
- 方式二: 缓存虚拟槽和node的关系
Pipeline(管道)
正常存储:
一百万条数据.
一次一个请求一个响应,时间浪费在网络请求中.
使用Pipeline后,所有数据一块请求和响应.
redis分布式 加锁和解锁
加锁:通过set参数加锁,只有key不存在才能获取这个值成功
jedis.set(String key, String value, String nxxx, String expx, int time)
jedis.set(lockKey,requestId,SET_IF_NOT_EXIST,SET_WITH_EXPIRE_TIME,expireTime);
lockKey: 锁,利用key唯一
requestId: 用来区分是哪个请求加的锁,可以使用UUID.randomUUID().toString()方法生成.判断这个参数,谁创建的锁谁来解锁.
SET_IF_NOT_EXIST:这个参数填的是NX,当key不存在时,我们进行set操作,若key已经存在,则不做任何操作.
SET_WITH_EXPIRE_TIME: 这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定.
expireTime: 与第四个参数相呼应,代表key的过期时间.
解锁: 使用lua脚本
使用过期时间防止死锁
自己持有的锁才能被自己释放
数据一致性问题(面试题)
删除redis数据时更新redis数据的最简单方式.
1. 先操作redis的数据再操作数据库的数据
并发情况下,多个请求,一个删除了,又一个访问,会重新写入redis. 方法一,使用队列(效率低) 方法二,延时双删,先删除缓存,操作完数据库再次删除,即使中间出现又创建redis的数据也会被删除.
2. 先操作数据库的数据再操作redis的数据
通过消息队列删除 重试删除(同步)
利用服务监听binlog的变化, 异步删除(异步)
热点数据发现
- 客户端
- 代理层 proxy
- 服务端
- 机器 packetbeat
缓存雪崩
大量key同时失效 ttl过期时间相同
方法一,设置过期时间不统一,添加随机数.
方法二,永不过期
方法三,缓存预更新
缓存穿透
缓存没有,数据库也没有
方法一: 即使数据库也没有,缓存中添加一条空的记录,查询到就知道数据库也为空.
方法二: 利用布隆过滤器解决,redis查询之前用布隆过滤器判断
布隆过滤器
利用位图+多个hash函数
如果布隆过滤器告诉我存在,他可能存在
如果布隆过滤器告诉我不存在,他一定不存在.
布隆过滤去存在误判率,默认0.03%
布隆过滤器解决判断海量数据中是否存在一个元素.
上一篇: 深入理解redis
下一篇: 搜索引擎项目——分词