年底了,你发年终奖了吗?你难过吗?不管是被动毕业还是主动毕业,生活总得继续吧?作为程序员,离不开Redis。谁让不如意的盘还这么慢?要想通过面试这一关,必须要掌握Redis。除了会用,还得了解它背后的原理。为什么?因为现在大家都在养蛊。生活中有很多无奈。逆水行舟,不进则退。如果您阅读过Redis相关书籍,本文将帮助您快速阅读。没看过没关系,缺的可以补。Redis能力:10W/sQPS(redis-benchmark)1w+长链接(netstat/ss)最复杂Zset6kw数据写入1k/s读取5k/s平均耗时5ms持久化(rdb)1.基本概述学习一个新语言,掌握它的基本数据结构和这些数据结构的API很重要。redis的这些数据结构类似于一种语言。常用的Redis数据结构有5种,一共10种。面试的时候,一般答5种就够了,但是另外5种是加分项。Stringstring哈希字典List列表Set集合ZSet有序集合。性能参考:《redis的zset有多牛?请把耳朵递过来》Pubsub发布订阅(不推荐,坑多)Bitmap位图GEO地理位置(限制使用,附近人多)Stream(5.0)(很像Kafka)Hyperloglog基数统计Redis协议Redis是RESP端的文本协议withCRLF(\r\n)RESP3(启用了redis6,并添加了客户端缓存)。Redis的底层数据结构在数据量小和大的时候往往是不一样的。关注大数据量的主要结构。String-sdsHash-(ziplist,dict)Set-(intset,dict)List-(ziplist,quicklist)ZSet-(ziplist+skiptableskiptable)Stream-(radix-treecardinality)skiptable引起了很多关注。Java中可以参考类似ConcurrentSkipListMap的实现。另:有序的Set在Java中叫做TreeSet,不过是用红黑树实现的,注意区别。Redis持久化模式生产环境一般只使用RDB模式。RDBAOF(类似Binglog行模式)混合模式:RDB+AOFO(n)commandkeys*hgetallsmemberssunion...集合大小不确定时推荐使用scanhscansscanzscan代替。另外,对于keys这样的危险命令,最好使用RENAME命令来屏蔽。性能优化unlinkdeletekey->异步避免阻塞管道批量传输,减少网络RTT->减少频繁网络交互多值指令(mset,hmset)->减少频繁网络交互关闭aof->避免io_wait扩展模式luaredis-modulemodulemode知道的人比较少,属于比较低级的开发。2.故障排除monitor命令keyspace-events订阅了某些Key事件。比如删除一条数据的事件,底层实现是基于pubsubslow日志,顾名思义,全量查询,非常有用--bigkeys启动参数Redisbigkey健康检查。使用scan方式执行,不用担心阻塞memoryusagekey,memorystatscommandinfocommand,注意instantaneous_ops_per_sec,used_memory_human,connected_clientsredis-rdb-toolsrdb离线分析3.淘汰策略如果是申请redisdba,这个问题答不上来直接淘汰。被动删除(只有获取到才删除并返回NIL为惰性删除)主动删除(100ms运行一次,随机删除持续25ms,类似Cron)->内存使用超过maxmemory,第三种情况触发主动清理策略,有是8个策略。注意redis已经有LFU了。默认情况下,volatile-lru从过期数据集中寻找最近最少使用的volatile-ttl,先从过期数据集中删除剩余时间短的Key。volatile-random从过期数据集中选择任意数据。删除数据集中最近不经常使用的数据。剔除使用频率最低的allkeys-lruallkeys-lfuallkeys-随机数据。No-enviction如果不设置maxmemory,Redis会一直使用内存,直到操作系统的OOM-KILLER被触发。4、集群模式单机单机多实例master-slave(1+n)master-slave(1+n)&sentinel(3或cardinality)RedisCluster(推荐,但限制使用)。参考:《与亲生的Redis Cluster,来一次亲密接触》网上推荐使用RedisCluster,随意外包和项目。具体搭建过程可参考:《好慌,Redis这么多集群方案,要用哪种?》大型twemproxycodis自研基于NettyRedis协议的管理平台:CacheCloud5。RedisFAQ分布式限流SessionAPI示例:zset排行榜、排序位图用户签到、在线状态地理位置、附近人流kafka类消息流hyperloglog每日访问ip统计缓存一致性为什么会出现一致性问题?写。缓存和数据库是两个不同的组件。只要涉及双写,就有可能只有一次写成功,导致数据不一致。更新。更新情况类似,需要更新两个不同的组件。读。读取时,需要保证从缓存中读取的信息是最新的,并且与数据库中的一致。删除。删除数据库记录时,如何删除缓存中的数据?建议使用:CacheAsidePattern读请求:先读缓存,再读db更改操作:先操作数据库,然后淘汰缓存涉及到复杂的事务和回滚操作,可以把淘汰放在最后。问题:缓存逐出失败!(概率很低,时序补偿)cachebreakdown影响很小。大流量下,大量请求读到一个无效的Key->RedisMiss->穿透到DB解决方法:使用分布式锁,只有第一个拿到锁的线程请求数据库,然后插入缓存缓存穿透效果,一般.访问一个不存在的Key->RedisMiss->渗透到DB解决方法:给对应的Key设置一个Null值,放入缓存中BloomFilter预判缓存雪崩影响:严重。大量Key同时失效|2.RedisCrash->RedisMiss->压力打击DB解决方法:在失效时间上加上一个相对随机数,保证Redis的分布式锁高可用Redis的分布式锁没那么简单.推荐使用redisson的redlock。最基本的指令是setnx。setnx->SETkeyvalue[EXseconds|PXmilliseconds|KEEPTTL][NX|XX][GET]分布式锁关键点:原子锁超时死锁读写锁故障转移最简单的Redis分布式锁代码(不严谨)。java端代码模拟加锁和解锁。publicStringlock(Stringkey,inttimeOutSecond){for(;;){Stringstamp=String.valueOf(System.nanoTime());布尔值存在=redisTemplate.opsForValue().setIfAbsent(key,stamp,timeOutSecond,TimeUnit.SECONDS);如果(存在){返回邮票;}}}publicvoidunlock(Stringkey,Stringstamp){redisTemplate.execute(script,Arrays.asList(key),stamp);}lua脚本解锁。localstamp=ARGV[1]localkey=KEYS[1]localcurrent=redis.call("GET",key)ifstamp==currentthenredis.call("DEL",key)return"OK"end6.Redis使用通用Java客户端lettuceSpringBoot默认,基于Netty事件驱动模型的jedis老式客户端,使用commons-pool完成线程池开发redisson非常丰富的分布式数据结构,包括锁,分布式Maps等。广泛使用Lua脚本?详解:Redis老了,你还在用什么古董客户端?使用规范根据公司情况定制剪裁,没有通用的规范。More:这可能是最中肯的Redis规范连接池的使用,不要频繁创建closeclientconnectionmessagesize限制messagebody在10kb以下,可以使用snappy,msgpack等压缩避免大key和hotkey不要使用O(n)指令不使用Zrange指令没有范围不使用数据库(容易覆盖数据)不使用高级数据结构(使用5种基本类型)不使用事务操作禁止长期监控springboot缓存Redis应该付出更多注意规范的缓存层抽象级别太高。如果需要操作底层数据结构,直接使用redisTemplate。Redis是多线程的吗?这取决于哪个阶段。数据操作阶段一直都是单线程的,即使是redis6。这篇文章分析了这个过程:和刚静聊Redis多线程:(完祝你好运!如果有帮助,请点个赞。作者简介:小姐姐品味
