Redis(Remote Dictionary Server)
视频教程:全面透析redis底层原理+redis分布式锁+企业解决方案+redis实战
redis是基于内存的,速度快;IO多路复用;有丰富的数据类型,合适的选择可以提高运行效率。
String
setnx加ex超时,配合lua,分布式锁。
有自增自减操作(INCR -1),SETNX,将对象序列化为JSON字符串后存储
缓存击穿中热点key加互斥锁的时候可以用SETNX来实现()
分布式中全局唯一ID,每天一个key(方便统计)、ID构造是 时间戳 + 计数器(自增实现)
MSET、MGET、INCRBY、SETNX
Hash
可重入锁,field是threadid,value是次数,配合lua。
无序字典,里面会有对应域和值,可对单个字段做独立的CRUD
批量:HMSET、HMGET
List
异步队列,生产者消费者,下的单进行消费,但是单一消费者。
双向链表,插入和删除快
可用BLPOP和RPUSH,或者BRPOP和LPUSH,实现阻塞队列,缺点:
Set
与hashset,可看作value为null的hashmap,无序、不重复,查找快,交集、并集、差集等
比如看自己跟别人的共同关注列表
SortedSet(ZSet)
每一条博客下面,按照用户点赞时间排序给用户排序。
底层实现是skiplist跳表+hash表
根据score排序,比如用来做排行榜等,不重复,查询快,里面有一个指令是incrementScore,每次查询这个key之后都会自增1,而不是说我每次自己取出来+1再放回去。
使用redis做延迟队列
使用一个延时队列,利用redis的zset(sort set,有序不重复集合,关联分数score进行排序),将提醒时间作为分数,提取符合条件的score对应的集合发起提醒(本文所述也是围绕这个方案)
1 | Boolean addFlag = redisTemplate.opsForZSet().add(QUEUE_NAME, messageStr, queueMessage.getDelayTime()); |
GEO和BitMap
==geo==获取附近商铺信息
==bitmap==比如每日签到,签到统计
注意
GEO是sortedset的实现
bitmap其实是==string数据类型==实现
新的数据类型Stream,消息队列
Redis Pipeline 原理及注意事项


缓存穿透
客户端请求的,在缓存中和数据库中都不存在
- 缓存空对象(+TTL)
- 布隆过滤(BitMap实现)(多一层前置过滤,存在误判)
缓存雪崩
同一时间大量缓存的key同时失效 或 Redis服务宕机,大量的请求到达服务器
针对不同的有不同的方法,给不同的key的TTL添加波动的随机值;Redis主从集群哨兵(能解决高并发读,高可用),分片集群(高并发写,海量数据存储)。
但是如果真的出现了,也要考虑。给缓存业务添加降级限流策略(消息队列固定请求大小,削峰填谷。判失败,拒绝服务器),给业务添加多级缓存。
缓存击穿
热点key都会首先缓存到redis中的。
热点key问题,被高并发访问并且缓存重建业务复杂(重建的时间内大量的访问)的key失效了。
解决:互斥锁(SETNX(),需要TTL防止程序异常。一致性)(加锁是查询数据库的时候加锁,而且这个key最好是加适当的时间呀,如果说他是在1天内秒杀,然后提前一天上架,那么久TTL设置成2天呗?);逻辑过期(不用TTl自己维护一个expire,也要获锁新开线程。可用性,无需等待,但是期间拿到的是旧数据)
HyperLogLog做UV统计
pipeline批量导入数据,pipeline的多个命令之间不具备原子性,mset和pipeline这样的批处理,如果是在集群中,那么批处理命令的多个key必须落在一个插槽中。
持久化
RDB和AOF
主从和哨兵(高可用、高并发读)
分片(海量数据存储、高并发写)
散列插槽,数据key与插槽绑定(16384个插槽分配到不同的实例,根据有小部分计算哈希值,取余)
多级缓存
OpenResty
Redis缓存预热
缓存同步
数据库的主从同步原理
设置有效期外失效,同步双写强一致性(修改完数据库修改缓存形成事务),异步通知
异步通知,第一种是基于==MQ==的异步通知,需要手写发布监听,然后更新;第二种是canal,把自己伪装成一个MYSQL的slave节点,从而坚挺binary log的变化,再把得到的变化信息通知给canal的客户端,进而完成对其他数据库的同步。所以要用canal,要先实现mysql的主从。
AOP:事务、动态代理
SDS动态字符串
redis没有直接使用c的字符串(获取长度需要遍历,非二进制安全,不可修改)
SDS中有buf[],一保存字符串长度,申请总字节数,不同头类型大小,可以动态扩容
IntSet
唯一、长度可变、有序、二分查找来查询
Dict
dict由三部分组成:哈希表、哈希节点、字典。一个dict中有两个哈希表bictht,一个用于存储,一个用于扩容再哈希使用。哈希表中是数组结合单向链表实现解决hash冲突,类似java的hashtable,当个数到达一定的时候,会扩容收缩(伴随再hash)。
非连续,碎片多
ZipList
一个特殊的“双端链表”,由一系列特殊编码的连续内存组成,用1字节或者5字节保存前一节点的长度而不是用指针记录前后节点。
QuickList
一个双端链表,链表中的每一个节点都是一个ziplist。(LinkedList+ZipList)使用的ziplist,解决了传统链表的内存占用问题(碎片),控制每一个zipilst大小,解决了连续内存空间申请的效率问题,中间节点可以压缩进一步节省内存。
SkipList跳表
链表,升序排序,节点包含多个后指针,跨度不同,前驱指针只有一个
RedisObject
Redis的任意数据类型的键和值都会被封装为一个RedisObject,也叫Redis对象。
五种数据结构是如何的
网络模型
为什么redis单线程
内存回收
有关发布订阅
首先要知道,redis也是可以做到消息队列的发布订阅的,一般的发布订阅是直接消息队列rabbitmq或者Kafka这些。
发布订阅:发布订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(如果有的话)存在。
redis: 轻量级,低延迟,高并发,低可靠性;
rabbitmq:重量级,高可靠,异步,不保证实时;
rabbitmq是一个专门的AMQP协议队列,他的优势就在于提供可靠的队列服务,并且可做到异步,而redis主要是用于缓存的,redis的发布订阅模块,可用于实现及时性,且可靠性低的功能。
Feed流
实践:本项目中使用推
作者写好后,查找到他的所有粉丝,然后给所有的粉丝对应的key中增加这篇文章,当然此处使用了zset,根据文章发布时间排序。
当然如果作者删除什么的需要处理。(我觉得可以懒处理,粉丝要看的时候从redis里面取出来然后比较存不存在,不存在的话redis删除,但是这样的话多次查询数据库还是累的)。
Redis锁从面试连环炮聊到神仙打架。
- Redis 做分布式锁的时候有需要注意的问题?
- 如果是 Redis 是单点部署的,会带来什么问题?
- 那你准备怎么解决单点问题呢?
- 集群模式下,比如主从模式,有没有什么问题呢?
- 你知道 Redis 是怎么解决集群模式也不靠谱的问题的吗?
- 那你简单的介绍一下 Redlock 吧?
- 你觉得 Redlock 有什么问题呢?
Kafka中,consumer 是推还是拉?
customer 应该从 brokes 拉取消息还是 brokers 将消息推送到 consumer,也就是 pull 还 push。在这方面,Kafka 遵循了一种大部分消息系统共同的传统的设计:producer 将消 息推送到 broker,consumer 从 broker 拉取消息。
push 模式,将消息推送到下游的 consumer。这样做有好处也有坏处:由 broker 决定 消息推送的速率,对于不同消费速率的 consumer 就不太好处理了。消息系统都致力于让 consumer 以最大的速率最快速的消费消息,但不幸的是,push 模式下,当 broker 推送的 速率远大于 consumer 消费的速率时,consumer 恐怕就要崩溃了。最终 Kafka 还是选取了 传统的 pull 模式。
在Java中发送kafka消息
1 | producer.send(new ProducerRecord<>("my-replicated-topic","key"+i,"value"+i)); |
Kafka集群消息积压问题和数据倾斜问题及处理策略
Kafka消息积压的典型场景:
比如,我们写的实时应用因为某种原因挂掉了,并且这个任务没有被监控程序监控发现通知相关负责人,负责人又没有写自动拉起任务的脚本进行重启。
那么在我们重新启动这个实时应用进行消费之前,这段时间的消息就会被滞后处理,如果数据量很大,可就不是简单重启应用直接消费就能解决的。
Kafka单分区生产消息的速度qps通常很高,如果消费者因为某些原因(比如受业务逻辑复杂度影响,消费时间会有所不同),就会出现消费滞后的情况。
此外,Kafka分区数是Kafka并行度调优的最小单元,如果Kafka分区数设置的太少,会影响Kafka consumer消费的吞吐量。
在使用Kafka producer消息时,可以为消息指定key,但是要求key要均匀,否则会出现Kafka分区间数据不均衡。
那么,针对上述的情况,有什么好的办法处理数据积压呢?
一般情况下,针对性的解决办法有以下几种:
a. 任务重新启动后直接消费最新的消息,对于”滞后”的历史数据采用离线程序进行”补漏”。
此外,建议将任务纳入监控体系,当任务出现问题时,及时通知相关负责人处理。当然任务重启脚本也是要有的,还要求实时框架异常处理能力要强,避免数据不规范导致的不能重新拉起任务。
b. 任务启动从上次提交offset处开始消费处理
如果积压的数据量很大,需要增加任务的处理能力,比如增加资源,让任务能尽可能的快速消费处理,并赶上消费最新的消息
如果数据量很大,合理的增加Kafka分区数是关键。如果利用的是Spark流和Kafka direct approach方式,也可以对KafkaRDD进行repartition重分区,增加并行度处理。
可以在Kafka producer处,给key加随机后缀,使其均衡。
一种解决消费海量Kafka Topic时数据倾斜的方案
为什么Redis的操作是原子性的,怎么保证原子性的?
为什么Redis的操作是原子性的,怎么保证原子性的?
对于Redis而言,命令的原子性指的是:
一个操作的不可以再分,操作要么执行,要么不执行。
Redis的操作之所以是原子性的,是因为Redis是单线程的。
Redis本身提供的所有API都是原子操作,
Redis中的事务其实是要保证批量操作的原子性。
多个命令在并发中也是原子性的吗?
不一定, 将get和set改成单命令操作,incr 。
使用Redis的事务,或者使用Redis+Lua==的方式实现
数据结构——Redis中的bitmap
redis中bit映射被限制在512MB之内,所以最大是2^32位。建议每个key的位数都控制下,因为读取时候时间复杂度O(n),越大的串读的时间花销越多。
Author: Jcwang
Permalink: http://example.com/2022/07/15/%E5%85%AB%E8%82%A1redis%E5%9F%BA%E7%A1%80/