Redis是键值对的数据库。常用的五种数据类型分别是字符串类型(string)、散列类型(hash)、列表类型(list),以及集合类型(set)、有序集合类型(zset)。Redis作为缓存主要有两个目的:高性能和高并发,因为内存天生就支持高并发。应用场景分布式锁(string)setnxkeyvalue,当key不存在时,将key的值设置为value并返回1。如果给定的key已经存在,setnx什么也不做,返回0。当setnx返回1时,它意味着获得了锁。操作完成后,delkey表示释放锁。如果setnx返回0,表示获取锁失败。计数器(字符串),例如知道每个问题的浏览器数量。setkey0incrkey//incrreadcount::{postid}每次读取getkey//getreadcount::{postid}获取阅读量分布式全局唯一id(字符串)分布式全局唯一id的实现方式有很多种,这里只介绍redis完成。每次获取userId时,将userId加1再获取,可以改进为如下形式:直接获取一个userId的最大值,本地缓存,慢慢累加,当userId的最大值快到了,然后获取一段,一个user服务宕机了,最多一小段userId没有被使用setuserId0incrusrId//return1incrbyuserId1000//return10001messagequeue(list)可以在list中进入并退出同时当key列表有元素时,会直接弹出,不会阻塞任何元素,直到等待超时或者找到可以弹出的元素。上面例子中的超时时间是10sbrpopkeyvalue10#实现方法2rpushkeyvalueblpopkeyvalue10新浪/推特用户消息列表(list)如果我们说小编li跟了a和b两条微博,a发了一条微博(编号100),执行如下命令:lpushmsg::li100b发送一条微博(号码200),执行如下命令:lpushmsg::li200如果想获取最新的10条消息,可以执行以下命令(最新消息必须在微博的最左边)list):#下标从0开始,[start,stop]为闭区间,全部包含lrangemsg::li09抽奖活动(set)#进入赛Activitysaddkey{userId}#获取所有抽奖用户,大rouletteturnsupsmemberskey#Drawcountwinnersandremovethemfromthelotteryspopkeycount#Drawcountwinnersandnotremovethemfromthelotterysrandmemberkeycount实现点赞、签到、点赞等功能(设置)#1001用户给8001个帖子点赞saddlike::80011001#取消点赞Listsmemberslike::8001#获取用户的点赞数scardlike::8001实现了如下模型,可能认识的人(设置)七个人关注sevenSub->{qing,mic,james}关注qingSub的青山人->{七,jack,mic,james}MicFollowersMicSub->{seven,james,qing,jack,tom}#返回sevenSub和qingSub的交集,即七和青山sintersevenSubqingSub->{mic,james}#我关注的人也关注他。下例中我是七#qing,在micSub中返回1,否则返回0sismembermicSubqingsismemberjamesSubqing#我可能认识人,下例中我是七#找出qingSub和sevenSub的区别,存入sevenMayKnow集合sdiffstoresevenMayKnowqingSubsevenSub->{seven,jack}电商产品筛选(集)每一个产品入库时都会创建他的品牌、尺寸、处理器、内存等静态标签列表#放上saviory700P-001和ThinkPad-T480这两个元素进入收藏brand::lenovosaddbrand::lenovosaviory700P-001ThinkPad-T480saddscreenSize::15.6Rescuery700P-001MechanicalRevolutionZ2AIRsaddprocessor::i7Rescuery700P-001MechanicalRevolutionX8TIPlus#获取Lenovo品牌,屏幕尺寸为15.6,并且处理器是i7(sinter是得到的集合的交集)sinterbrand::lenovoscreenSize::15.6processor::i7->Rescuery700P-001RankingEdition(zset)Redis的zset是为了做排行榜而生的ds、好友列表、去重、历史记录等业务需求。#user1的用户得分为10zaddranking10user1zaddranking20user2#取得分最高的3个用户zrevrangeranking02withscores过期策略定期删除redis会将每个有过期时间的key放到一个单独的字典中,它会周期性遍历这个字典删除过期的key。定时删除策略Redis默认每秒执行十次过期扫描(每100ms一次)。过期扫描不会遍历过期字典中的所有键,而是采用简单的贪心策略。从过期字典中随机选择20个key;删除20个key中过期的key;如果过期密钥的比例超过1/4,重复步骤1;惰性删除除了常规遍历外,还会使用惰性策略来删除过期的key,所谓惰性策略就是当客户端访问key时,redis会检查key的过期时间。如果它过期,它会立即被删除,并且不会返回任何东西给你。定时删除是集中处理,懒删除是零星处理。为什么要用正删+懒删两种策略呢?过期删除。假设在redis中放了10万个key,并且设置了过期时间,你每隔几百毫秒检查10万个key,那么redis基本就死了,CPU负载会很高,消耗在你检查过期的key上传了,但是问题是定期删除可能会导致很多过期的key到时候被删除,那怎么办呢?所以懒得删了。也就是说,当你拿到一个key的时候,如果设置了过期时间,redis会检查这个key是否已经过期?如果过期了,此时会被删除,什么也不会退还给你。不是到时候把key删除了,而是你查询key的时候,redis会懒查的。通过以上两种方式的结合,保证了过期的key会被kill掉。因此,在使用了以上两种策略后,下面的现象就不难解释了:数据明明已经过期了,但它仍然占用着内存。可能有些朋友遇到过内存淘汰策略的问题。为什么Redis里面的数据没有了?因为Redis是把数据放在内存中的,所以内存是有限的。比如redis只能用10G,如果你往里面写20G的数据,你会怎么办?当然会杀掉10G的数据,然后会保留10G的数据。什么样的数据会被销毁?保留哪些数据?当然是去掉不常用的数据,保留常用的数据。Redis提供的内存淘汰策略如下:noeviction不会继续为写请求服务(DEL请求可以继续服务),读请求可以继续。这样可以保证数据不会丢失,但是会导致线上业务无法继续。这是默认的消除策略。volatile-lru尝试淘汰设置了过期时间的键,最少使用的键首先被淘汰。没有设置过期时间的key不会被淘汰,这样需要持久化的数据就不会突然丢失。(这个用的最多)volatile-ttl同上,只是淘汰策略不是LRU,而是key剩余生命周期ttl的值。ttl越小,淘汰的优先级越高。volatile-random同上,但是淘汰的key是过期key集合中的一个随机key。allkeys-lru不同于volatile-lru。该策略淘汰的key对象都是keyset,而不仅仅是过期的keyset。这意味着没有过期时间的密钥也将被淘汰。allkeys-random同上,但淘汰策略是随机key。allkeys-random同上,但淘汰策略是随机key。持久化策略Redis数据存储在内存中。如果Redis崩溃了,所有的数据都会丢失,所以必须提供持久化机制。Redis的持久化机制有两种,第一种是快照(RDB),第二种是AOF日志。快照是全量备份,AOF日志是连续增量备份。Snapshot是内存数据的二进制序列化形式,在存储上非常紧凑,而AOFlog记录的是内存数据修改的指令记录文本。AOF日志在长期运行中会变得非常大。当数据库重启时,需要加载AOF日志进行commandreplay,这个时间会非常长。因此,需要定期进行AOF重写,对AOF日志进行瘦身。RDB通过Redis主进程fork子进程,让子进程进行磁盘IO操作,实现RDB持久化。AOF日志存储的是Redis服务器的顺序命令序列,AOF日志只记录修改内存的命令记录。即RDB记录数据,AOF记录指令。RDB和AOF应该如何选择?不要只使用RDB,因为那样会导致你丢失大量数据,因为RDB每隔一段时间就会备份数据。不要只使用AOF,因为有两个问题。第一,通过AOF冷备份不如RDB恢复快;其次,RDB每次生成数据快照简单粗暴,健壮性更强,可以避免AOF等复杂备份和恢复机制的bug。使用RDB恢复内存状态会丢失大量数据,重放AOP日志很慢。Redis4.0引入了混合持久化来解决这个问题。将rdb文件的内容和增量的AOF日志文件一起存储。这里的AOF日志不再是全量日志,而是从开始持久化到持久化结束这段时间发生的增量AOF日志。通常,这部分AOF日志很小。因此,当Redis重启时,可以先加载rdb的内容,然后重放增量AOF日志,完全替代之前的AOF全量文件重放,大大提高了重启效率。缓存雪崩和缓存穿透什么是缓存雪崩?假设有一个系统如下,高峰期请求每秒5000次,4000次去缓存,只有1000次落在数据库上,数据库并发1000次每秒是正常指标,可以正常工作,但是如果缓存宕机的话,每秒5000个请求全部落到数据库上,数据库马上就死掉了,因为数据库最多可以抵抗每秒2000个请求。如果DBA重新启动数据库,它会立即被新请求杀死。这是缓存雪崩。如何提前解决缓存雪崩:redis高可用,master-slave+sentinel,rediscluster,避免totalcrash。进行中:本地ehcache+hystrix限流&降级防止MySQL被杀。之后:redis持久化,缓存数据的快速恢复。什么是缓存穿透?如果客户端每秒发送5000个请求,其中4000个是黑客恶意攻击的,即使在数据库中查不到。比如用户id是正数,黑客构造的用户id是负数。如果黑客继续每秒发送这4000个请求,缓存将无法工作,数据库很快就会被杀死。如何解决缓存穿透无法查询到的数据也放在缓存中,值为空,比如set-999""总而言之,缓存雪崩意味着缓存失效。所有的请求都打到数据库上,数据库被秒杀。缓存穿透就是查询一个一定不存在的数据,从存储层查不到的数据不写入缓存,这样会导致每次请求时都在存储层查询不存在的数据,并且缓存将丢失。意义。
