Redis常见问题

参考文章:

https://www.cnblogs.com/php-no-2/p/9396965.html

https://www.cnblogs.com/jasontec/p/9699242.html

https://www.cnblogs.com/yuanermen/p/5717885.html (集群搭建)

推荐视频

https://www.bilibili.com/video/av50514461

 

中文网

http://www.redis.cn/

http://redisdoc.com/list/index.html

在线测试

http://try.redis.io/

适用场景

减轻数据库压力,提高响应速度,加大吞吐量,线程安全控制。

热点数据缓存,静态数据缓存,高频变化数据缓存(点击数等),分布式session,分布式/集群共享数据,时效性数据,分布式限流,限时功能,分布式锁,秒杀,BitMap统计状态1/0,交/并/差集合,最新动态(SortedSet +时间戳),消息队列,订阅发布,HASH购物车(hincr实现线程安全数量更改)

 

单指令线程安全

因为是单线程串行执行,所以自然地保证了单一指令的原子性,可见性,有序性。保证了线程安全执行。

复合命令需要配合事务/LUA保证原子性。

 

Redis如何实现单线程支持高并发?

C语言实现,纯内存操作,多路复用

下图左边IO部分,ServerSocket通过 NIO多路复用非阻塞监听Socket,提交给任务QUEUE

右边事件分派器处理不同的任务纯内存操作 ,且单线程没有上下文切换。

 

Redis是单线程的,如何提高多核CPU的利用率?

一个机器通过不同端口开多个实例

 

Redis有哪些数据结构?

字符串String、字典Hash、列表List、集合Set、有序集合SortedSet,BitMap位操作(布隆过滤),

HyperLogLog、Geo、Pub/Sub

 

BitMap结构模型?

是一个bit数组(默认都是0),offset偏移量是数组下标。bit值只有0/1。

setbit  key  offset  value

getbit  key  offset 

countbit key   统计此BitMap中1的数量。

bitop 实现多key之间 AND 、 OR 、 NOT 、 XOR

https://www.cnblogs.com/xuwenjin/p/8885376.html

https://blog.csdn.net/u011489043/article/details/78990162

 

Redis 常用指令

http://www.redis.cn/commands.html#

命令即使key不存在也会正常返回失败值不会报错,返回如0、null等。

甚至有些命令没有key也可以正确执行,比如:incr,sismember。

利用此特性可以让所有Set/Add命令执行时,再去创建对应KEY,而不用先创建key或判断key存在。类似懒加载

 

String:set,set nx px,get,mget,decr,incrby,append,scan key [begin] match **  [count]

Hash:hset,hget,hgetall,hincrby,hlen,hkeys, hvals,hscan key [begin] match *key* [count]

Set:sadd, srem ,spop, smembers, sismember, scard, sinter, sunion, sdiff (交并差,以redis中保存的文本判断相等,差以第一个集合为主),sscan;

Sorted Set : zadd,zrange(top),zrem,zcard,zrank,zscore,zincrby

List : lpush,rpush,lpop,rpop,lrange(分页)

 

Redis 用SCAN代替Keys命令, 查询key列表避免阻塞

http://redisdoc.com/key/scan.html

redis是单线程执行命令,在生产环境中 Keys *,因为key太多导致阻塞几分钟。

这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。

扫描集合s1 ,返回第一个值为已扫描游标,返回0说明已经全部扫描,返回大于零整数说明本次未扫描完成,下次扫描需要从此游标位置开始

sscan s1 0

1) "0"
2) 1) "1"
    2) "2"
    3) "3"

 

https://blog.csdn.net/fly910905/article/details/78942357

https://blog.csdn.net/zhxdick/article/details/78268027

Redis实现条件过滤

http://www.redis.cn/commands/scan.html

SCAN cursor [MATCH pattern] [COUNT count]

  • SCAN 命令用于迭代当前数据库中的key集合。
  • SSCAN 命令用于迭代SET集合中的元素。
  • HSCAN 命令用于迭代Hash类型中的键值对。
  • ZSCAN 命令用于迭代SortSet集合中的元素和元素对应的分值
//扫描string 不用指定key
scan 176 MATCH *11* COUNT 1000

//扫描set中元素 指定set的key
sscan myset 0 match f*  

SORT排序

http://www.redis.cn/commands/sort.html

 

Redis中保存过多的KEY,需要整理规划。

可以在设置key的时候,将同类关系的KEY 加入相同的 "前缀:" ,方便归类管理。

例如:set p:sss 123  ; set p:f:aaa 123

 

Redis中保存数据库表?

方案1:利用HASH结构,KEY=表名,子KEY=各行主键id,VALUE=此行数据json对象。整张表保存到一个HASH中。

方案2:利用HASH结构,KEY=表名:id,子KEY=属性名,VALUE=属性值。相对于方案1,方案2把每一行数据独立保存为一个HASH。使得单个KEY不会过大,集群时无法数据均匀分布  ,而且更新不好控制。

方案3:利用String结构,KEY=表名:id:属性名。使用MSET+MGET来一次性获取同一行所有属性。

 

HASH有优缺点?

优点:1 整体方便管理 2 避免key冲突  3 内存少,因为子键不用保存TTL等信息

缺点:1 子KEY不独立,不好单独操作 2 集群时无法数据均匀分布  

 

 Redis如何做内存消耗优化

答:尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面.

 

TTL

1 建议一般Key都要设置TTL,避免内存超载。Redis通过 定期随机删除(默认100ms) + 惰性删除(操作key时判断) 来实现TTL

2 灵活运用TTL特性,实现限流、事件开始结束标志、限时令牌等等功能

 

一个Redis实例最多能存放多少的keys

每个实例至少存放了2亿5千万的keys。一个key或是value大小最大是512M。换句话说,Redis的存储极限是系统中的可用内存值。

 

常见题目

https://blog.csdn.net/u010682330/article/details/81043419 

 

优化操作过于频繁的Redis?

使用Pipeline合并请求,减少TCP链接次数。客户端允许将多个请求一次发给服务器,过程中而不需要等待请求的回复,在最后再一并读取结果即可。

  • pipeline机制可以优化吞吐量,但无法提供原子性/事务保障,而这个可以通过Redis-Multi等命令实现。
  • 部分读写操作存在相关依赖,无法使用pipeline实现,可利用Script机制,但需要在可维护性方面做好取舍

https://www.cnblogs.com/littleatp/p/8419796.html

 

什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?

持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。

Redis 提供了两种持久化方式:RDB(默认) 和AOF 

Redis4.0之后有了混合持久化的功能,将RDB的全量和AOF的增量做了融合处理,这样既保证了恢复的效率又兼顾了数据的安全性。

RDB:

rdb是Redis DataBase缩写

功能核心函数rdbSave(生成RDB文件)和rdbLoad(从文件加载内存)两个函数

AOF:

Aof是Append-only file缩写

每当执行服务器(定时)任务或者函数时flushAppendOnlyFile 函数都会被调用, 这个函数执行以下两个工作

aof写入保存:

WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件

SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。

存储结构:

  内容是redis通讯协议(RESP )格式的命令文本存储。

比较

1、aof文件比rdb更新频率高,优先使用aof还原数据。(aof记录指令log,rdb间隔时间拷贝DB)

2、aof比rdb更安全也更大

3、rdb性能比aof好,rdb恢复更快速

4、如果两个都配了优先加载AOF

 

Pipeline有什么好处,为什么要用pipeline?

可以将多次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性。使用redis-benchmark进行压测的时候可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。

 

Redis常见性能问题和解决方案? 

 https://www.cnblogs.com/chenpingzhao/p/6859041.html

通过主从架构实现一主多从,读写分离水平扩容

1.Master必须持久化,避免master重启后把空信息同步到所有从机,造成数据丢失。 

2.如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。   

3.为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。   

4.尽量避免在压力较大的主库上增加从库  

5.为了Master的稳定性,主从复制不要用图状结构,用单向链表结构更稳定,即主从关系为:Master<--Slave1,Slave2<--Slave3.......,这样的结构也方便解决单点故障问题,实现Slave对Master的替换,也即,如果Master挂了,可以立马启用Slave1做Master,其他不变。

 

使用过Redis做异步队列么,你是怎么用的?

一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。

如果对方追问可不可以不用sleep呢?list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。

如果对方追问能不能生产一次消费多次呢?使用pub/sub主题订阅者模式,可以实现1:N的消息队列。

如果对方追问pub/sub有什么缺点?在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。

 

Redis 有哪些架构模式?讲讲各自的特点

 单机版

特点:简单

问题:

1、内存容量有限 2、处理能力有限 3、无法高可用。

主从复制

Redis 的复制(replication)功能允许用户根据一个 Redis 服务器来创建任意多个该服务器的复制品,其中被复制的服务器为主服务器(master),而通过复制创建出来的服务器复制品则为从服务器(slave)。 只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步 给从服务器,从而一直保证主从服务器的数据相同。

特点:

1、master/slave 角色

2、master/slave 数据相同

3、降低 master 读压力在转交从库

问题:

无法保证高可用

没有解决 master 写的压力

哨兵(主从升级版)

推荐启用sentinel2

Redis sentinel 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移。其中三个特性:

监控(Monitoring):    Sentinel  会不断地检查你的主服务器和从服务器是否运作正常。

提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。

自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会选出新主机,开始一次自动故障迁移操作。原主机修复上线后,会自动变为从机。但是迁移过程可能丢失数据

特点:

0、主从架构实现一主多从,读写分离、水平扩容,提高吞吐量和可拓展性

1、保证高可用、自动换主、自动故障迁移

2、监控各个节点

3、API通知

缺点:

主从模式,切换时可能丢失数据;

没有解决 master 写的压力;slave只分担读工作,写只有master。

必须3个节点起步(quorun=2),因为同意故障转移由投票完成,两个节点挂掉一个后无法完成此工作。

springboot配置哨兵:https://blog.csdn.net/m0_37367413/article/details/82018125

 

主从模式常见数据丢失问题:

0 master没有持久化,重启后集群全部同步成空。

1 集群切换master时可能数据丢失

2 脑裂,因为原mater与集群网络失联,所以集群选举新master。但是原master还正常运行,部分客户端仍然写原master。

   设置:min-slaves-to-write 1  要求master至少有一个slave

              min-slaves-max-lag 10    所有slave同步延迟超过10s以上,master不接收任何请求,等待slave同步跟上。此时client也要适当降级限流异步减少写请求。

 

集群(proxy 型):

Twemproxy 是一个 Twitter 开源的一个 redis 和 memcache 快速/轻量级代理服务器; Twemproxy 是一个快速的单线程代理程序,支持 Memcached ASCII 协议和 redis 协议。

特点:1、多种 hash 算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins 

2、支持失败节点自动删除

3、后端 Sharding 分片逻辑对业务透明,业务方的读写方式和操作单个 Redis 一致

缺点:增加了新的 proxy,需要维护其高可用。

 

failover 逻辑需要自己实现,其本身不能支持故障的自动转移可扩展性差,进行扩缩容都需要手动干预

集群(直连型):

从redis 3.0之后版本支持redis-cluster集群,Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。客服端链接任意节点都相当于访问集群

集群原理与搭建:https://www.cnblogs.com/cjsblog/p/9048545.html

.conf 中设置 cluster enable yes,通过redis-3.0.0.gem(官方ruby脚本搭建集群)执行集群命令

replicas 1 代表每个主节点有一个从节点 

 

增删节点:https://www.cnblogs.com/wt11/p/5918728.html

增加主节点后,  删除主节点前,都要执行./redis-trib.rb reshard 重新分配/转移数据槽

特点:

1、无中心架构(不存在哪个节点影响性能瓶颈),少了 proxy 层。

2、数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。

3、可扩展性,可线性扩展到 1000 个节点,节点可动态添加或删除。

4、高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做备份数据副本,比如四台集群,cluster自动配置两主两从

5、实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave到 Master 的角色提升。

缺点:

1、资源隔离性较差,容易出现相互影响的情况。

2、数据通过异步复制,不保证数据的强一致性

3、最小三主三从6节点

 

Redis集群方案什么情况下会导致整个集群不可用?

答:有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用。

 

什么是一致性哈希算法?什么是哈希槽?

这两个问题篇幅过长 网上找了两个解锁的不错的文章

https://www.cnblogs.com/lpfuture/p/5796398.html

https://blog.csdn.net/z15732621582/article/details/79121213

 

使用过Redis分布式锁么,它是怎么实现的?

先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。

 

如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?

set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!

SET key value PX time(ms) NX 

或可以使用LUA保证原子性 

RedisTemplate+lua脚本 实现分布式锁:https://blog.csdn.net/u014495560/article/details/82531046

 

缓存并发?缓存穿透?缓存击穿?缓存雪崩?何如避免?

缓存并发

主要是控制并发操作的原子性问题,比如先查询,再判断,再修改这种复合操作,在高并发时如果不控制原子性会经常出错。

1 分布式锁  2 LUA脚本  3 通过修改后API返回值判断

缓存穿透

一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。一些恶意的请求会故意查询不存在的key,请求量很大,造成缓存不命中,一直访问DB。就会对后端系统造成很大的压力。这就叫做缓存穿透。

如何避免?

1:对查询结果为空的KEY也进行缓存,缓存时间TTL设置短一点,或者该key对应的数据insert了之后清理缓存。

2:对一定不存在的key进行过滤。可以把所有存在的key放到一个大的Bitmap中,查询时先通过该bitmap过滤。(布隆过滤)

布隆过滤:有误判率,原因是hash碰撞和槽长度有限。但是比方案1更加节省内存。

缓存击穿

当没有缓存时,可能同时有大量线程访问数据库去查询相同的内容,对数据库造成瞬时压力。

解决:

可以在同种数据的查询代码中加分布式锁,这样保证一个SQL执行完后,其他都可见。这样可以减轻数据库的瞬时压力,但是会造成大量线程竞争锁的情况,大量读操作串行化。

还有一种是通过CAS无锁话机制,更新数值状态。判断是否有缓存。推荐这种可以避免串行化读的问题

实现代码:

https://gitee.com/sw008/Test1/blob/master/dubboservice/src/main/java/com/example/dubboservice/service/impl/SimServiceImpl.java

查询缓存
没有则AtomicLong.CAS取得DB查询权利?
获权:二次检查缓存  (存在)?是返回:否 ->查询DB->更新缓存->放权->CountDowbLatch唤醒
无权:CountDowbLatch等待唤醒->查缓存

缓存雪崩

当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统停顿崩溃。

如何避免?

1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

2:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期

3:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。批量添加KEY时 TTL=固定值+随机数 。

 

Redis事务

http://www.redis.cn/topics/transactions.html 

watch、unwatch、multi、exec、discard 

可以实现CAS,按顺序地串行化整体执行而不会被其它命令插入(redis单线程)。multi命令使Redis将这些命令放入queue,而不是执行这些命令。当放入queue失败时(例如:语法错误),EXEC命令可以检测到并且不执行。一旦调用EXEC命令成功,那么Redis就会一次性执行事务中所有命令,redis单线程执行queue中间不会插入其他命令,但是中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。相反,调用DISCARD命令将会清除事务队列,然后退出事务。EXEC命令的返回值是一个数组,其中的每个元素都分别是事务中的每个命令的返回值,返回值的顺序和命令的发出顺序是相同的。watch必须写在multi之前。

//CAS 伪代码
//注意MULTI中所有命令 返回NULL,包括GET
//所以 取值操作 要在MULTI前执行

WATCH mykey  //监视 要在MULTI前执行
val = GET mykey
val = val + 1

MULTI
SET mykey $val //加入queue
EXEC  //queue执行

SpringBoot 配置

# Redis数据库索引(默认为0)
spring.redis.database=0  
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379  
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=50  
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1  
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8  
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0  
# 连接超时时间(毫秒)
spring.redis.timeout=0 

 

Redis应用场景注意点

1 秒杀业务防止超卖:保证查询和更新等复合操作的整体原子性

秒杀思路:

简单:MQ+数据库乐观锁(幂等)

复杂:Redis限流+Redis int/List/Set 限制库存/用户(可以使用LUA脚本) + MQ+数据库条件乐观锁(幂等)

推荐使用LUA脚本把【判断用户是否已秒杀?+减库存成功?+添加用户集合】做成原子性操作。限制超卖和重卖。

简单实现:https://blog.csdn.net/zhurhyme/article/details/79046470

Redis限流:限制用户5秒内不超过5次请求。 可以为每个请求用户设置一个KEY int,TTL=5s,拦截请求一次incr并时判断其返回值,超过次数请求失败。每过5s此Key失效重新计次数。

秒杀:根据用户提交的产品信息,获取到redis中需要的key值,查询缓存,判断当前库存是否大于零,如果>0,判断当前的set集合中是否用该用户ID。如果没有,decr对库存减1得到减后库存量(或List.pop),并且将用户的ID放入集合中,如果减后返回库存<0(或List.pop返回NULL),提示用户商品已售完信息。注意高并发下不能用 kc == 0做判断。如果集合中已经存在该用户id,则不做任何处理(考虑库存回滚1),直接处理下一个请求直到最后库存售完。上面的过程也可以利用redis事务和watch功能完成对数据一致性的控制即超卖问题。用户信息存入SET,甚至可以控制请求IP,通过MQ做流量消峰,最终数据库端还可以用乐观锁再进行超卖保护(update XX set  kcshl=kcshl-1  where kcshl>0 and spid='XX')

//简单模拟 实际业务更为复杂
//注意:不要限制当前进程的redis连接数
//skillList预装被抢购商品信息(例如 10个phoneX)

volatile boolean have=true;

public void skill(String userid) {
	if (have) { 
                 //从skillList中抢购商品
		String imei=redisTemplate.opsForList().leftPop("skillList");	
		if (imei != null && !"".equals(imei)) {	
                        //放入成功用户集合skillSet-user(可以再判断用户唯一性,重复抢购则把商品imei返回skillList)
			redisTemplate.opsForSet().add("skillSet-user",userid); 
			log.info("手机imei:"+imei+" user:"+userid);
                        //可以放入MQ,交给下游服务记录数据库(幂等+乐观锁)
                        //数据库结果,决定是否最终秒杀成功
		}else {
			have=false;
		}
	}	
}

    1.1 Redission 提供全局AtomicInteger实现类 可以作为库存,防止超卖

    1.2 RedisTemplate  提供 opsForValue().decrement(key,Long):Long  方法实现 更新并返回计算结果,判断返回值>=0再生成订单,可以作为库存,防止超卖。但是这种可能高并发瞬间被减成负值,返回值要使用范围判断,不能使用=0来判断。

    1.3 RedisTemplate 提供 opsForList().leftPop(key) :String 使用List装入库存数量相同个商品对象,抢购时使用POP方法,防止超卖,List空则返回NULL。对比1.2更加严格

    1.4 分布式锁控制,但是效率降低(锁一定放在事务之外,否则会出现超卖,因为隔离性问题)

    1.5 SET存放抢单用户信息,防止多抢。不要用LIST,因为SET可以去重复

2 分布式锁实现

   2.1 SET key value PX time(ms) NX 实现分布式锁  (Redission 提供丰富的Lock和JDK并发组件接口的实现)

   2.2 可以通过原子性的整数更新实现 自旋锁/CAS 等问题

3 作为热点数据缓存,注意防止穿透,配置淘汰规则

 

LUA实现复合redis指令原子性执行

http://www.redis.cn/commands/eval.html 

串行化执行保证:原子性、有序性、可见性、减少通讯次数

使用LUA脚本可以原子的执行redis指令集,可以实现简单的逻辑判断,保证并发的原子性执行。

复合指令原子性:redis是单线程,执行LUA脚本时其他redis指令排队等待脚本全部执行完成,这样LUA脚本中的所有指令执行过程中不被打断,因此在编写脚本的过程中无需担心会出现竞态条件.

LUA不会回滚,中间出现错误,已经执行的命令就是既定事实。

lua脚本相对于redis事务的优势:

1 redis事务是进入queue中一起执行,每一步的结果是最后整体返回,所以中间无法通过java代码对每一步执行结果进行判断。

   redis事务只能通过watch监视KEY, 发生变化queue不执行。

   lua脚本在保证原子性同时,还可以根据每一步的执行结果,选择不同代码逻辑分支,更加灵活。

2 lua脚本执行效率高。事务的watch相当于乐观锁CAS,可能会出现反复失败重试的情况。而LUA的特性不会出现此问题。

redis执行lua脚本: https://www.cnblogs.com/huangxincheng/p/6230129.html

可以直接EVAL执行,也可先执行scriptLoad缓存脚本再执行EVAL,返回SHA加密代码, 之后可以执行EVALSHA+代码直接使用缓存脚本,减少网络传输的开销。

EVAL script numkeys key [key ...] arg [arg ...]
 <1> script:     你的lua脚本

 <2> numkeys:  参数中前几个是KEY

 <3> key:         redis中各种数据结构的替代符号

 <4> arg:         你的自定义参数


eval "local var=redis.call('get',KEYS[1]) return var" 1 skey
注意:脚本里边用单引号。
一个简单的lua脚本 KEYS,ARGV都是脚本入参

local num=redis.call('incr',KEYS[1]) 
if (tonumber(num)==1) then 
  redis.call('expire',KEYS[1],ARGV[1]) 
  return 1 
elseif (tonumber(num)>tonumber(ARGV[2])) then 
  return 0 
else 
  return 1 
end


redis对象是REDIS客户端为LUA脚本添加的内置对象,在REDIS中执行的lua自带这个对象
redis.call :执行redis命令,不捕获异常,报错直接终止
redis.pcall :执行redis命令,捕获异常

红包功能

https://www.jianshu.com/p/b58ed2fe6976?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

秒杀

https://blog.csdn.net/zhurhyme/article/details/79046470

RedisTemplate+lua脚本 实现分布式锁:https://blog.csdn.net/u014495560/article/details/82531046

 

使用Lua脚本+Redis实现分布式可重入锁样例

原贴https://github.com/qiurunze123/miaosha/blob/master/docs/redis-good.md

RedisTemplate执行LUAhttps://blog.csdn.net/weixin_38003389/article/details/89049135

RedisTemplate+lua脚本 实现分布式锁:https://blog.csdn.net/u014495560/article/details/82531046

思路是使用setnx这是Key的值,值是一个唯一标识。通过值判断当期线程是否可以重入。

 

 

Redis的内存用完了会发生什么?

redis设置配置文件(redis.conf)的maxmemory参数,可以控制其最大可用内存大小(字节)。

那么当所需内存,超过maxmemory怎么办?这个时候就该配置文件中的maxmemory-policy出场了。其默认值是noeviction。

下面我将列出当可用内存不足时,删除redis键具有的淘汰规则。

规则名称

规则说明

volatile-lru

使用LRU算法删除一个键(只对设置了生存时间的键)

allkeys-lru

使用LRU算法删除一个键

volatile-random

随机删除一个键(只对设置了生存时间的键)

allkeys-random

随机删除一个键

volatile-ttl

删除生存时间最近的一个键

noeviction

不删除键,写返回错误,读正常(默认)

注意这里的6种机制,volatile和allkeys规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据,后面的lru、ttl以及random是三种不同的淘汰策略,再加上一种no-enviction永不回收的策略。

LRU算法,least RecentlyUsed,最近最少使用算法。也就是说默认删除最近最少使用的键。

但是一定要注意一点!redis中并不会准确的删除所有键中最近最少使用的键,而是随机抽取3个键,删除这三个键中最近最少使用的键。那么3这个数字也是可以设置的,对应位置是配置文件中的maxmeory-samples

使用策略规则:

1、如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用allkeys-lru

2、如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random

 

REDIS与SQL双写一致性方案

方案1:先更新SQL,再删缓存。最终一致性,只更新数据库和数据库中的消息表。定时任务/消息队列处理消息表中的缓存删除任务。由读请求添加新缓存。高并发存在风险

方案2:延时双删策略。先删缓存(保证删除成功),再更新SQL,延迟/MQ  删缓存。在1基础上加强了缓存数据的删除操作,高并发仍然存在风险。

方案3:同KEY串行化,强一致性最有保证,但是吞吐量影响较大。

            实现方式:1-分布式锁同步+分段锁思想;2-同KEY同一队列+单一消费者

方案4:mysql-binlog订阅,注意有序性

https://blog.csdn.net/qq_37779352/article/details/82977921

常用排查指令

info:显示系统、版本、内存使用等信息,最后显示CPU使用 ,Keyspace各分区状态等信息

keys代表当前key数量,expires代表设置TTL的数量

redis-cli –bigkeys 或 findBigKeys :找到各个数据类型中内存最大的key

SET的使用场景 

 

ZSET的使用场景 

 

posted @ 2018-11-26 15:25  sw008  阅读(765)  评论(0)    收藏  举报