一、redis为什么快
-
1.基于内存:内存读写效率远高于磁盘读写
-
2.存储形式:键值对的内存数据库,哈希表结构 O(1)时间复杂度可以获取值
-
3.单线程:避免上下文切换
-
4.IO多路复用 :单个线程处理多个客户端链接
-
5.编码:支持多种数据结构及编码,根据元素数量,有阀值,大于小于阀值的编码不同
-
6.渐进式ReHash:把一次性大量拷贝(数组移动)的开销,分摊到多次处理请求的过程中
-
7缓存时间戳:由一个定时任务,每毫秒更新一次缓存,获取时间从缓存中直接拿
二、redis的应用场景
常用基本数据类型(8种)
名称 | 英文名 | 作用域 |
---|---|---|
字符串 | String | 缓存、计数器、分布式session,分布式锁 |
哈希 | hash | 存放对象 |
列表 | list | 消息队列、文章列表 |
集合 | set | 标签、随机数、社交图谱 |
有序集合 | zset | 排行榜 |
Bitmaps | Bitmaps | 布隆过滤器 |
HyperLogLog | HyperLogLog | uv |
Geo | Geo | redis3.2推出 用于存储地理位置信息 |
1.字符串(string)
- 命令的时间复杂度:
- del、mset、mget支持多键操作,时间复杂度和键个数相关为 O(n)
- getrange的 -> O(n)
- 剩余全是O(1)
- 缓存:具有支持高并发的特性,加速读写,降低后端压力
- 计数器:实现快速计数、查询缓存的功能、同时数据可异步落地到其他数据源
- 分布式会话:(共享session):将用户的session进行集中管理
- 分布式锁:使用redis setnx来实现分布式锁
2.哈希(Hash)
- 适用于存储对象,比string开发效率更高
3.列表(list)
用来存储多个有序字符串
- 消息队列:lpush + brpop 命令组合实现阻塞队列:
生产环境客户端使用lpush从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式的”抢“列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。 - 文章列表:有序,且支持按照索引范围获取元素
4.集合(set)
- 标签:同一标签:对于用户体验和增强用户粘性比较重要
- 随机数:抽奖活动
- 社交网络:点赞、粉丝数、共同好友、喜好、推送、下拉刷新等
- sunion命令 : 返回多个set类型的并集
- sinter命令:返回多个set类型的交集集
- sdiff命令:返回多个set类型的差集
5.有序集合(zset)
- 排行榜:多维度:时间、浏览量、获赞数
6.BitMaps(位数组实现):
bitmaps应用场景
- 几亿用户系统签到
- 去重登录次数统计
- 某用户是否在线状态
命令:setbit、getbit、bitcount
bitmaps原理:redis内构一个足够长的数组,每个数组只能是0和1两个值,数组下标表示用户id,这个几亿长的大数组就可以通过下标和元素值(0和1)来构建一个记忆系统 - 布隆过滤器:用于判断一个元素是否在一个集合中
7.HyperLogLog(基于概率有误差)
用于计算一个很大的元素集合的基数,基数是指集合中不同元素的数量。这种结构是用来处理大数据的,因为它会以极小的内存空间(通常每个元素只需要12字节)来计算基数。
- UV : 统计每个网页每天的uc数据,不精确的去重计数方案,误差0.81%
8.Geo
常用结构命令
- 1.geoadd:添加地理位置信息
eg:geoadd cities 13.361389 38.115556 "Palermo" - 2.geopos:获取地理位置信息。
eg:geopos cities "Palermo" - 3.geodist:获取两个地理位置的距离。
eg:geodist cities "Palermo" "Catania" km - 4.georadius:根据用户给定的经纬度坐标来获取位于指定范围内的地理位置元素
eg:georadius cities 15 37 100 km WITHCOORD WITHDIST WITHHASH - 5.georadiusbymember:根据位置元素所对应的坐标来获取位于指定范围内的地理位置元素。
eg:georadiusbymember cities "Palermo" 100 km WITHCOORD WITHDIST WITHHASH - 6.geohash:获取一个或多个位置元素的 Geohash 值。
eg:geohash cities "Palermo" "Catania"
三、为什么redis6.0引入多线程
redis6.0之前
- redis的瓶颈不是cpu,受制于内存、网络
- 提高redis性能,pipeline(命令批量)
- 单线程,维护成本低,不需关注线程安全
- 惰性rehash减少阻塞
redis6.0引入多线程之后:
本质:多线程任务 分摊到Redis 同步IO中,读写负载。
- 1、小数据包。数据-》内存 响应时间 100ns 8w-10wQPS(极限)
- 2、针对大的公司,需要更大的QPS,IO的多线程(内部执行命令还是单线程)
- 3、为什么不采用分布式架构—很大的缺点。
服务器数量多,维护成本高。Redis命令 不适用 需要数据分区,无法解决热点数据读写的问题。
数据倾斜、重新分配、扩容、缩容,更加复杂
4.redis的高级功能
- 1.慢查询
快速定位系统中的慢操作,监测发生时间、耗时、命令的详细信息 - 2.Pipeline:命令批量
- 3.wathc命令:
确保事务中的key有没有被其他客户端修改过,才执行事务,否则不执行(类似于乐观锁) - 4.redis + lua语言实现限流
- 5.分布式锁:setnx:加锁设置过期时间 + 看门狗续期
- 6.高并发高可用:
- 1.主从复制:
提供了复制功能,实现了相同数据的多个Redis副本。每个主节点可以对应多个从节点,复制的数据流只能由主节点复制到从节点 - 主从复制的问题
- 需要手动晋升子节点,同时需要修改应用方的节点地址。
- 主节点的写能力收到单机限制
- 主节点的存储能力收到单机的限制
- 1.主从复制:
- 7 哨兵(redis sentinel)
主从复制模式下,主节点故障,需要人工将从节点晋升为主节点。
2,8版本开始提供哨兵架构解决此问题。
原理是哨兵通过发送命令(ping命令),等待Redis服务器响应,如果在指定时间内,主机Redis无响应,从机则判断主机宕机,选举从机上位,从而监控运行的多个Redis实例。- 1.心跳机制:每个sentinel每秒一次向主从服务器发送ping命令,获取状态信息,心跳检测
- 2.判断master节点是否下线(默认30s)
- 3.基于Raft算法选举领头sentinel
- 4.故障转移:选择一个slaver节点来作为master
- 1.选择优先级最高的(replica-priority配置项)越小,优先级越高
- 2.优先级相同,选择offset(偏移量)最大的,越多同步数据越多
- 3.如果offset也相同,选择 run id 较小的
通过以上4个步骤,s实现由Redis Sentinel自动完成故障发现和转移,实现自动高可用
Redis哨兵模式的优缺点
- 哨兵模式优点:最大的优点就是主从可以自动切换,系统更健壮,可用性更高;
- 哨兵模式缺点:最大的缺点就是还要多维护一套哨兵模式,实现起来也变的更加复杂增加维护成本;
5.redis的事务
本质:按照顺序串行化执行队列中的每个命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
顺序性、一次性、排他性 执行一个队列中的一系列命令。
命令 | 描述 |
---|---|
EXEC | 执行所有事务块命令 |
DISCARD | 取消事务、放弃执行事务块所有命令 |
MULTI | 标记一个事务块,开启事务 |
UNWATCH | 取消watch命令对所有key的监视 |
WATCH | 监视key,如果在事务执行前,该key被其他命令改动,事务将被打断 |
代码实例: |
//构建redis连接
Jedis jedis = jedisPool.getResource();
//标记一个事务块,开启事务
Transaction transaction = jedis.multi();
try {
transaction.set("name1","江先进");
// int n = 10/0;
transaction.set("name2","张硕");
//执行事务
transaction.exec();
}catch (Exception e){
//取消事务
transaction.discard();
e.printStackTrace();
}
redis提供了简单的事务功能,将一组需要一起执行的命令放到 multi 和exec两个命令之间。multi命令代表事务开始,exec命令代表事务结束,另外discard命令是回滚
回滚机制上,redis只能对基本语法错误进行判断,运行时错误无法回滚
6.redis的过期策略及内存淘汰机制
7.缓存穿透、缓存击穿、缓存雪崩
1.缓存穿透:数据库和redis都不存在
解决方案
- 1.参数校验,屏蔽非法参数
- 2.数据库查询为空,可缓存一个空值或默认值
-
- 使用布隆过滤器快速判断数据是否存在
2.缓存击穿:单个key没有或过期,同一时间查询这同一条数据并发量过多
解决
- 热点数据用不过期,或者通过异步线程在每次热点数据快要过期时,进行续期
- 使用互斥锁,避免大量请求同时查询数据库
- 熔断、降级、防止系统崩溃
- 还可以考虑对重要的热点数据进行多级缓存
3.缓存雪崩:同一时间,缓存大面积失效
原因
1.redis失效、宕机(故障)
- 搭建Redis集群,主从架构
- RDB持久化、IOF持久化
- 加入缓存组件:EHCache,搭建多级缓存(容易高并发的数据存入)
- 加入限流组件:hystrix,超过一定流量后,增加请求限制(保护数据处理层)
2、Redis大量key的ttl过期
ttl(过期时间)岔开,增加随机值,避免同一时间全部失效。
- 热点数据永不过期,或者通过异步线程在每次热点数据快要过期时,进行续期
- 数据的过期时间不要全一致,设为一定范围内的随机时间
- 并发量不高,可加入队列或者锁,限制同一时间访问数据库的阈值
- 分布式部署,将热点数据打散分不到多个节点
- 如果是缓存中间件宕机了,需要尽可能保证其高可用性,可以搭建redis集群,提前做好报警机制
8.什么是bigkey,会有什么影响
key对应的value所占内存空间较大
比如字符串类型:单个value值特别大,一般认为超过10kb就是bigkey,和具体的ops相关
非字符串类型:哈希、列表、集合、有序集合,体现在元素个数过多
危害
- 内存空间不均匀
- 超时堵塞:单线程操作bigkey比较耗时
- 网络拥塞:每次获取bigkey产生的网络流量较大
例如:一个bigkey为1MB,每秒访问为1000,则每秒产生1000MB的流量,普通千兆网(按照字节算是128MB/s)的服务器是灭顶之灾,而且服务器通常会采用单机多实例的方式来部署,可能会对其他实例造成影响。
解决方案:value拆分
redis如何解决key冲突
- 1.业务隔离
- 2.key的设计:业务模块+系统名称+关键(id),针对用户可以加入(userid)
- 3.分布式锁:多客户端并发写入key
- 4.时间戳:key拼接时间戳,根据时间戳保证多个客户端的业务执行顺序
redis怎么提高命中率
- 1、提前加载
- 2、增加缓存的存储空间,增加缓存的数据
- 3、调整缓存的存储类型
例:对象通过Hash存储,而不用String。
根据业务做适当调整。 - 4、调整缓存的存储类型
- 定时任务更新
- MySQL通过检测binlog,将消息推送到Redis,更新缓存
- 通过Mq,业务更新修改数据时,通过MQ发送消息,消费更新缓存
redis持久化方式有哪些方式,有什么区别?
持久化:将数据写入磁盘,,避免因进程退出而造成 的数据丢失,下次重启时通过持久化文件恢复数据。
RDB(快照)
通过快照(内存中数据在某一时刻的状态记录)的方式实现持久化,根据快照的触发条件,将内存的快照写入磁盘,以二进制的压缩文件进行存储。
缺点:每隔一段时间触发持久化,数据安全性低
AOF(独立日志)
以独立日志的方式记录每次写的命令,重启时重新执行aof文件中的命令恢复数据
AOF重写机制:AOF文件的大小达到某个阈值时,会将其中指令进行压缩。(如果有对于某个key多次的变更指令,则仅保留最新的数据指令)。
*优化:
因为AOF重写过程中需要读取当前内存中所有键值数据,性能较低,redis将其放在一个后台子线程中完成。
为了避免重写过程中出现数据变动,主进程的数据变更需要追加到AOF重写缓冲区中,等到AOF重写完成后,再把AOF重写换乘区里面的内容追加到新的AOF文件中。
缺点:AOF文件可能过大,性能较差
如果执行bgrewriteaof命令,将内存中已有的数据以二进制格式存放在AOF文件中(模拟RDB),后续命令亦然采用AOF追加方式。
生产环境中一般采用两种持久化机制混合使用。
将内存中数据快照存储在AOF文件中(模拟RDB),后续再以AOF追加方式。
如果仅作为缓存使用,可以承受几分钟数据丢失,可以使用RDB,对主程序性能影响最小
如何保证缓存与数据库双写一致性:
1.新增数据类
新增数据时,数据会直接写入数据库,不用对缓存做任何操作;此时缓存没有新增数据,而数据库中是最新值。
2.更新缓存类
1.先删除缓存,后更新db
- 先淘汰缓存-》再写数据库—〉休眠1秒,再次淘汰缓存
延迟双删可以有效避免缓存不一致问题
以下是伪代码实现延迟双删
redis.delkey(x)
db.update(x)
Thread.sleep(n)
redis.delkey(x)
3.读取binlog日志:
异步删除,更新缓存
- 可以使用canal将binlog日志采集发送mq队列中
- 通过ack机制确认处理这条更新消息,删除缓存,保证数据一致性
redis集群方案
redis常见性能问题和解决方案
1、持久化 性能问题
早期仅支持全量复制->部分复制(一台机器性能开销过大)
因此开始配置主从 :主节点不再做持久化而是交给从节点来做
2、数据比较重要,开启AOF。策略最好配置每秒同步。
3、主从复制 流畅,建议同一个局域网内操作,负责网络开销过大
4、尽量避免主库压力过大,增加从库
5、主从复制 尽量不要使用网状结构、线性结构
什么情况下可能会导致redis阻塞
- 客户端阻塞:命令执行时间过长: keys* Hgetall smembers 时间复杂度O(N)
- BigKey删除:需要释放大量占用内存 zset(100万的元素 删除大概需要2s)
- 清空库:flushdb
- AOF日志同步写,记录AOF日志
- 从库 加载RDB文件
- redis尽量部署独立的服务器中
线上redis响应慢处理思路:
- 1、紧急处理方案,扩容
- 2、生产环境查看Redis内存使用率,分析一定时间段内key数量变化
分析是否是大量数据未设置过期时间,或者是因为新版本迭代引起 - 3、清除bigkey,优化生成bigkey的代码块,调整未设置过期时间的代码块
- 4、根据业务场景调整淘汰策略
如何保证redis高可用:
主从模式:
- 部署多台redis服务器,主从复制保证数据副本一致,主库通过rdb文件发送从库实现复制
- 主从采用读写分离,主库写操作,从库读操作
- 主库宕机,手动切换从库称为主库,通知应用方更新主节点地址
哨兵模式
作用
- 监控:周期性ping主从库,检测是否挂了,标记下线状态
- 自动选主切换:多个从库按照一定规则选作主库
- 通知:选出主库,把主库信息发送给其他从库及应用方,重新建立连接
由一个或多个哨兵实例组成哨兵系统,监控其他Redis节点的同时,哨兵实例之间也互相监控。
哨兵之间通过发布订阅机制组成集群。一主多从
缺点:无法实现在线扩容,并发压力受限于单个服务器的资源配置。
26.3 Redis Cluster
哨兵模式解决了自动切换主从的问题,但是没有解决在线扩容的问题。
本质:Redis Cluster实现了Redis的分布式存储,每个节点存储不同的数据,实现数据分片。
引入Slot槽实现数据分片,每个节点分配一个Slot区间,当我们存取Key的时候,Redis根据key计算得到Slot值,找到对应的节点进行读写。多主多从。
布隆过滤器
布隆过滤器可以应对缓存穿透问题
数据结构:一个很长的二进制向量和一组Hash映射函数组成。
作用:检索一个元素是否在一个集合中,空间查询效率比一般的算法要好的多。
缺点:有一定误识别率和删除困难。
原理:集合A中有n个元素,利用K个哈希散列函数,将A中每个元素映射到一个长度为a位的数组B中不同位置上。这些位置上的二进制数均设置为1。经过这个K个哈希散列函数映射后,发现其k个位置上的二进制数全部为1,这个元素很可能属于集合A。
Redis的理解
- 本质:基于Key-Value存储结构的非关系型数据库
- 数据类型:提供5种基本数据类型,String、set、zset、list、Map。
- 性能:基于内存存储,并且在数据结构上做了大量优化,IO性能较好。
- 作用:作为应用与数据库之间的分布式缓存组件。
- 高可用:提供了主从复制、哨兵、以及集群方式实现高可用,集群中通过hash槽的方式实现了数据分片,进一步提升了性能。
原文链接:https://blog.csdn.net/Zyw907155124/article/details/129830935
标签:缓存,redis,Redis,命令,数据,节点 From: https://www.cnblogs.com/Skyvswfq/p/18159099