事务
存在的问题:当多个线程对同一个key进行操作
线程A | 线程B |
1 set name x | 2 set name y |
3 get name // y | 4 get name // y |
线程A对name操作后,紧接着线程B对name也进行了操作,导致A获取的name值不是自己更新的值。
将线程A的操作都放在一个事务中。
开启事务,将事务中的命令暂存入队列,到真正执行时执行。
指令:
multi | 事务块开始 |
discard | 取消当前事务块中的所有命令, 该指令只存在mutil和exec之间 |
exec | 执行代码块中所有命令。 当事务块中有错误指令则所有指令都不执行,有运行错误时(如对String进行list操作)能执行正确指令,错误指令不执行,且已执行指令不能回滚 |
watch key [key ...] | 对key添加监视锁,key在exec执行之前没有发生变化则执行事务,否则不执行。 watch出现在事务外,只能监视key是否发生变化,不能监视key具体的值。 |
unwatch | 取消对所有的key的监视 |
分布式锁
redis分布式锁就是解决之前提到的分布式系统中即使存在已经是synchronize的对象或代码同样会出现线程安全问题。
加锁: setnx lock_keyname 1
设置锁过期:
expire lock_keyname t (秒)
pexpire lock_keyname mt(毫秒)
释放锁: del lock_keyname
原理:不同的服务器对同一业务代码需要进行原子性执行时,获取当前业务代码的锁,获取不到时意味着有程序正在执行当前代码块,直到此锁被释放,没有被锁时才能执行,执行的同时加锁setnx lockname 1。
但是当出现一台服务器在加锁后宕机未释放锁的情况时,其他服务器便会一直获取不到执行此业务代码的权利,于是便出现了死锁。
为了避免死锁的发生,在加锁的同时会给锁加上自动过期时间。由于操作都是毫秒级的,所以锁的过期时间不宜过大,
推荐时间:最大耗时*120%+平均网络延时*110%。
数据过期
超过了有效期的数据就会成为过期数据。过期的数据根据删除的策略不同会在内存中停留的时间存在差别。
查看当前数据剩余存活时间:
ttl key
具体时间 :具有时效性的数据
-1 : 永久有效
-2 : 已过期||被删除||未定义的数据
过期数据删除策略:定时删除、惰性删除、定期删除
定时删除:key过期后,由定时器立即执行删除操作。不管此时CPU的负载高低都会执行,用时间换空间。
惰性删除:key过期后不做处理,到下次访问到此key时才执行删除操作。节约CPU的性能,内存压力大,用空间换时间。
定期删除:周期性轮询redis库中的时效性数据,利用过期数据占比的方式控制删除的频度(随机抽查,重点抽查)。
生产中使用惰性删除和定期删除组合的方式。
逐出算法
redis使用内存存储数据,在执行每一个命令前都会调用freeMemoryIfNeeded()方法检查是否内存充足,如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理出存储空间。数据清理的策略称为逐出算法。
简言之,就是redis根据哪种删除策略为指令的成功执行清理出内存空间。
当然,redis并不是100%能清理出足够的空间,如果清理不成功的话则反复执行,当对所有数据尝试过后还不能达到执行指令要求,则返回OOM(out of memory)。
配置:
maxmemory <bytes>,最大使用内存,默认为0,表示无限制,通常设为内存的50%以上;
maxmemory-samples int, 一次扫描的key数量,不配置时会扫描全库(消耗性能),通过随机获取数据的方式成为待检的杀出数据;
maxmemory-policy xxx,删除策略,对被挑选出来的数据进行删除的策略;
删除策略有如下几种分类:
1、易丢失的数据(可能会过期的数据)
volatile-lru : 挑选最近最少被使用的数据淘汰
volatile-lfu : 挑选最近使用频率最少的数据淘汰
volatile-ttl : 挑选要过期的数据淘汰
volatile-random :随机挑选数据淘汰
2、检测全库数据
allkeys-lru :全库挑选最近最少被使用的淘汰
allkeys-lfu :全库挑选最近使用频率最低的淘汰
allkeys-random :全库随机挑选数据淘汰
3、放弃数据逐出(可能会引发OOM)
no-enviction (redis默认)