美好只能封装在记忆的信封之中,喜悦犹如弹指之间飞逝的花瓣。终究只属于当下和日后的回忆。往事不能抹去,当下才是新的征程与起点。
网站最初通常不会存在高并发的情况,使用最简单的LNMP架构即可满足网站中的需求,但随着网站运营时间的累加,用户量的增多,网站在应对大量的用户请求时。将会出现卡顿、5xx系列错误、页面加载缓慢等问题。最初网站的流畅运行将随风而逝,眼下就需要程序员解决网站因为并发而造成的一系列问题。
因为单一使用数据库来保存数据的系统是面向于磁盘,磁盘读/写速度比较慢的问题而存在严重的性能弊端,一瞬间成千上万的请求到来,需要系统在极短的时间内完成 成千上万次的读/写操作,这个时候往往不是数据库能够承受的,极其容易造成数据库系统瘫痪,最终导致服务宕机的严重生产问题。
为了克服上述的问题,Web项目通常会引入NoSQL技术,这是一种基于内存的数据库,并且提供一定的持久化功能。
redis作为一款高性能的Key-Value的键值对缓存数据库,因丰富的数据类型而广泛应用于项目不同的应用场景。redis可以支持每秒十几万此的读/写操作,并且还支持集群、分布式、主从同步等配置,原则上可以无限扩展。
但多数情况下,绝大多数开发者只是单纯用Redis做缓存的保留使用,对于其它的数据类型基本都避而不见,但是往往采用Redis其它的数据类型可以代替其应用业务中 需要数据库提供的关系型数据。例如:投票、排行榜、粉丝统计、消息队列、发布订阅等等。这些场景的取代可以大幅度的提高网站的响应速度。
如果只是单纯的接触缓存还无妨,但往往实际场景中用到Redis的地方很多,关键是在于自身如何去接入Redis到网站中。
1、使用最简单,提升最缓慢
任何一门技术想快速入门,莫过于它的使用,因为这个过程中的,你的目的很明确,我就需要知道它大致的操作步骤、使用条件。入门后在是熟练操作的步骤,总结为自己的经验内容。
但这样往往有个问题,就是对于项目的业务场景分析不深刻,没有根据业务的情况选择合适的内容,而是万能的套模板式的开发,久而久之就感觉像在做简单的业务开发,没有什么实质性的突破,感觉自己的提升非常的缓慢。
这就是所谓的认知偏见,在你意识中Redis只可用于缓存,那么任何的业务条件下都是用它按照set 、get的方式来解决实际问题,对于业务场景下面的过程就少了业务场景下结果的分析。但随着业务快速的发展很快就会出现瓶颈问题。
例如:今日头条的青云计划中,每天中奖的名单会有100-1000个,同时还有分类的问题,一级、二级、三级,如果按照使用的方式可能就是根据你每个分类+ID值作为key,value就是固定长度标题+文章内容(也可分开设计key+value)。 虽然设置为缓存了,那如果需要展示的时候怎么好查询呢?
如果设置的时候每个分类对应一条记录对于一个key-->value,那你查询就需要规则匹配,这样很容易发生阻塞问题,同时每个分类下面的1 2 3三级的文章,如果在value里面设置对应的标识,取值出来还需要进行修改。费时又费力。
那如果把业务拆分成不同的分类、1 2 3级、标题。我们就可以用有序集合来确定分类名称,对应的标题,等级,用Hash结构做文章内容存储、发布时间、作者、浏览量等。这样是不是就可以按照业务的组成来选择对应的数据呢?
从而让我们更好的去理解业务。对于redis的认识也更加深刻,思维上也不在是按照普通的方式,而是根据业务场景选择合适的工具来做
2、好的方法可以节省时间
俗话说“工欲善其事,必先利其器”。好的数据类型就是我们的工具,如果在开始阶段对业务的分析,决定了选择什么好的数据类型,那么再去做的时候就可业务条件编写即可。而不是在书写的时候就是那缓存说话,这样你会花费很多的时间去修改对应的数据存储机构到Redis中。
因为普通的key->value的模式就像当与一对一的模式,如果你有其他的需求那么你知道在对应的value中做标记达到你需要的存储结构。同时效率也会很低的。但往往绝大多数开发者都不会提前的构思,直接动手写,如果有经验的工程师还好,因为别人知道书写的方向,但初学者就是会不断的碰壁,碰壁之后自己也没有任何的积累过程。后面遇到同样的问题,开发效率也不会高到哪里去。
3、价值产生不一样
任何一个人都是喜欢优秀的人,如果你做的事情都是满大街都有的东西,没得自己的方法与经验。那你的价值如何体现呢?
比如:几十年前的时候,通讯不发达,那时候有一个手机,可能是成功的人了。就在于绝大数人都没有手机这个东西。“物以稀为贵”就是这个理
如果问到你对Redis的使用,张口闭口就是缓存,而不在理会其它的数据类型,用于解决业务场景中的问题,这样的情况下如何证明自己熟练Redis呢?同时对于Redis的优化和高级、底层这些内容没了解,如何应对难度的问题呢?
总是说自己不会,那有过深入的研究准备吗?一切都停留在初级的阶段不打破,何来提升。 因为这才是固之根本所在。
一、数据结构不将就,好钢用在刀刃上
使用好Redis的第一步,需要对Redis的数据方式有全新的认识。认识每个数据类型的结构特点。同数据结构的组成上对应到业务数据存储上,在由业务数据特点对应到所适合的数据类型有那些。有时候选择好工具后,会事半功倍。
以下就是Redis的5大基础类型特点:
1、String 可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M。
常用场景:分布式session会话、计数器、接口限速、分布式锁等。
特点:对于数字递增处理、字符串处理都可以,因为它是Kye->Value关系的结构。就是相 当于对一个key的描述,前提是要找好key属于要准备操作什么数据类型。
常用命令:
SET key value GET key MSET key1 value1 [key2 value2] MGET key1 key2 INCR key DECR key SETNX key value #只有key 不存在时,才设置key的值
2、Hash 键值对集合,即编程语言中的Map类型。适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值。
常用场景:存储、读取、修改用户属性、购物车等
特点:Hash的结构类似于与字典结构,有一个key指定名称,value用于描述这个可以的信息,一般用于描述对象的基本信息。
例如:购物车里面商品ID、商品数量、用户ID、销量、关注等这些信息都算是购物车需要的信息结构。采用它灵活性是非常高的
常用命令:
HSET%20key%20field%20value HGET%20key%20field HGETALL%20key HMSET%20key%20field1%20value1%20[field2%20value2] HMGET%20key%20field1%20[filed2]
3、List 链表(双向链表)。存储数据的特点犹如一个有序列表的形式,由左、右边可插入数据到其中。
常用场景:最新消息排行等功能、消息队列、评论列表、令牌桶算法等。
特点:List结构关键部分它是一个双向链表,可以双向的添加信息到列表中,使用的时候在以出队的方式取出,所以只要满足操作的条件即可。同时列表也可做类似消息的排队处理操作。
常用命令:
LPUSH key value1 [value2] #将一个或多个值插入到列表头部 LPOP key #移出并获取列表的第一个元素 RPUSH key value1 [value2] #在列表尾部添加一个或多个值 RPOP key #移除并获取列表最后一个元素 LREM key count value #移除列表元素 LRANGE key start stop #获取列表指定范围内的元素
4、Set 由哈希表实现,用于存储多个字符串的元素,但和列表不同的是集合中不允许有重复的元素,并且集合中的元素是无序的。
常用场景: 标签、共同好友、共同好友等
特点: 集合的内部有hash表实现,这个很重要。也就是它的特别和hash的字典结构差不多,只不过它相当于内部没有key的指定,直接是信息的存储。把结果堆积在一起。
常用命令:
SADD key member1 [member2] #向集合添加一个或多个成员 SDIFF key1 [key2] #返回给定所有集合的差集 SINTER key1 [key2] #返回给定所有集合的交集 SUNION key1 [key2] #返回所有给定集合的并集 SISMEMBER key member #判断 member 元素是否是集合 key 的成员 SMEMBERS key #返回集合中的所有成员 SREM key member1 [member2] # 移除集合中一个或多个成员
5、Sorted Set:有序集合。将Set中的元素增加一个权重参数score,元素按score有序排列。数据插入集合时,已经是进行天然排序。
常用场景:排行榜、带权重的消息队列、时间线等
特点:存储结构特点像hash的结构,只不过是把hash内部的key变为了Score用于技术的分值。但是数据存储结构还是一致的,后面选择它的时候可以用于排行、权重这一块的业务,内部有一个计数的选项来保持内容的结果。
常用命令:
ZADD key score1 member1 [score2 member2] #向有序集合添加一个或多个成员,或者更新已存在成员的分数 ZINCRBY key increment member #有序集合中对指定成员的分数加上增量 increment ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] #通过分数范围返回有序集合指定区间内的成员 ZINTERSTORE destination numkeys key [key …] #计算给定的一个或多个有序集的交集,并将结果集存储在新的有序集合 key 中 ZUNIONSTORE destination numkeys key [key …] #计算给定的一个或多个有序集的并集,并存储在新的 key 中
对于数据结构更多是需要明白每个数据类型的组成,当拿到业务情况时。首先拆解业务组成的属性,从而用Redis的数据类型来代替原来需要MySQL结构化数据实现的业务,从而提升性能。同时应对一些情况特殊的场景,例如:秒杀用队列的时候、分布式时采用session共享会话等等
说到底Redis再好,它的本质就是用于缓存数据,如果请求没有在Redis里面找到缓存的内容,那么同样还是不能减轻数据的压力。同时如果内存存储一些冷数据也不能为那些高频率使用网站的用户提供服务。
所以提升缓存的命中率显得异常重要。那么如何来提升缓存的命令率呢?
那我们可设想,既然缓存的数据是需要通过Key的方式获取,那么我们在服务端进行key的监控,把那些不经常使用的key换成热点数据的key。同时缓存是为了提供业务的快速访问,但网站中热点的业务并不是所有的业务都需要等等。
归纳起来就是每个请求应该在哪里去找到key? 这个key使用的频率高吗?这个key会受那些因素影响后而导致缓存的命中率降低呢?
以下是一些因素问题的影响:
1. 业务场景
缓存适合“读多写少”的业务场景,反之,使用缓存的意义不大,命中率会很低。缓存时间越长,命中率会越高。时效性要求越低,就越适合缓存。
2. 更新策略
缓存的粒度越小,命中率会越高。举个实际的例子说明:
当缓存单个对象的时候(例如:单个用户信息),只有当该对象对应的数据发生变化时,我们才需要更新缓存或者让移除缓存。而当缓存一个集合的时候(例如:所有用户数据),其中任何一个对象对应的数据发生变化时,都需要更新或移除缓存
3. 清理策略
对于持续运行的Redis服务器来说, 服务器需要定期对自身的资源和状态进行必要的检查和整理,有三种不同的删除策略,立即删除会短时间内占用大量cpu,惰性删除会在一段时间内浪费内存,所以定时删除是一个折中的办法。
(1)立即清理。在设置键的过期时间时,创建一个回调事件,当过期时间达到时,由时间处理器自动执行键的删除操作。
(2)惰性清理。键过期了就过期了,不管。当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key
(3)定期清理。每隔一段时间,对expires字典进行检查,删除里面的过期键。
4. 缓存容量
缓存的容量有限,则容易引起Redis自身的缓存被淘汰淘汰策略。
5. 缓存故障
缓存节点故障,也会引起缓存失效,所以业内比较典型的做法就是通过一致性Hash算法来均衡分布缓存,或者通过集群采用节点冗余的方式。
6、监控Redis缓存操作命令
首先可通过info命令来分析缓存的率是否满足业务,如果太低那么就可以采用monitor命令或者第三方工具(如:redis-faina)分析操作key的访问频率,进而动态更改业务场景需要访问key的类型。
内存可以说是服务器最宝贵的资源运行地,所以对于Redis使用过程中,应该来优化Redis的内存,从而使其发挥出更大的性能。
那这个Redis的内存应该如何优化呢? 首先存储任何的缓存都是基于Redis的数据类型进行选择,但Redis设置key的时候选择对应的类型就显得的很重要。设置key和value的时候也不要过长等
就应该从Redis缓存的组成结构,淘汰缓存上做到避免无用数据占用缓存、内存碎片、数据类型使用内存上等问题来做分析
如下一些常用的思路
Redis主进程的内存消耗:
3.1对象内存:所有key对象长度 + 所有value对象长度
3.2缓冲内存:
3.3内存碎片:
3.4内存回收策略:
不单要使用,而是要会用。会用Redis可以帮助提升自己能力,展示自己的价值。很多时候应该先接触在代入到实际的工作场景中去使用,考虑的时候先进行知识的搜捕。然后与实际的场景业务进行类比处理。更多是知与行的结合