数据类型:
String Hash {"key":""val} List set {A,B,C} SortedSet
String:
指令:get set mget mset setnx(有不加) setex(时间/秒)
incr 、incrby。
应用场景:大部分业务场景都适用。
特殊使用:BitMap统计。BitMap的使用指令:set(key,offset,value(0/1)) getbit(offset) bitcount(key) bifield bitfield_ro
Hash:
指令: hset key field value, hget key filed, hmset , hmget
应用场景:用户信息类型统计(一个属性一个key,比直接string要小)。
List:(双链表)
指令:lpush,lpop, rpush,rpop.
set:
指令sadd ,
常见问题:
用 Jackson2JsonRedisSerializer 序列化的时候存入对象会返回LinkedHashMap?
--> 没有使用json工具读写的,默认是LinkedHashMap,使用了特定工具保存为String的,就是String
缓存穿透(解决方法一般有:布隆过滤器,缓存空对象,id校验过滤,增强id复杂度,热点参数限流,逻辑过期(需要一个锁来实现缓存重构))
缓存雪崩:同一时间段大量key同时失效或Redis服务宕机,大量服务到达数据库造成巨大压力
---> 给key添加随机TTL,添加多级缓存(nginx,tomcat,浏览器),限流降级
缓存击穿:热点数据,在大量并发的情况下时间到期,缓存重建时间长,大量并发请求打入数据库。
--> **互斥锁(优点:简单,没有额外内存开销;缺点:性能下降,死锁风险), 逻辑过期(优点:无需等待 ;缺点:一致性差(拿到锁后另外开启线程更新缓存,返回旧数据),额外缓存开销 ) **
超卖问题(更新数据时剩余数量小于0):
乐观锁(): 任务线程不安全问题不一定发送,不加锁,只是更新数据时去判断有没有其他线程对数据做了修改。 如果没有则认为安全,否则说明已经发生安全问题。
乐观锁的实现方案:
CAS:查询库存后, 更新库存的条件上加上一个 库存数量和开始查询到的一值(或大于0)
悲观锁: 认为线程安全问题一定会发生,在操作之前要先获取锁,保证按顺序指向,如 synchronized,Lock
单体项目的并发安全:
一个JVM内部通过获取某个key实现悲观锁
集群环境下用rdis实现安全:
办法:Mysql Redis ZoorKeeper
Redis解决:(确保获取锁后的操作的原子性)
V1: 用setnx的特性,每个用户根据id设置一个key
前置:setnx特定的key只能有 一个线程会成功
风险:
获取锁(setnx key)成功后,释放之前万一服务出错(例如宕机),锁就永远得不到释放(del key) -> 设置锁的操作时,同时设置过期时间
如果单个线程执行时间过长,超出了过期时间,那么其他线程就可以趁虚而入。此后,最开始的线程删除key,此时有可能会有其他线程在第二个线程还在执行的时候创建key,造成不安全。V2: 给Key设置包含线程id的val, 所有释放锁前观察当前val是否与预设值的相同,不同则不释放,相同则释放
为什么Lua脚本实现多条Redis命令的原子操作:
多条reids指令同时执行存在并发修改数据的隐患,redis 的官方文档中有描述lua脚本在执行的时候具有排他性,不允许其他命令或者脚本执行,类似于事务。但是存在的另一个问题是,它在执行的过程中如果一个命令报错不会回滚已执行的命令,所以要保证lua脚本的正确性。
小妙招:
Redisson基于redis实现可重入锁(省去自己实现)
redis序列化器默认是jdk序列化器,使用默认的序列化器会造成存储时出现乱码问题
标签:缓存,Redis,笔记,指令,线程,key,序列化 From: https://www.cnblogs.com/habc706/p/16908467.html