暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Redis知识点整理

lamdaxu 2020-07-08
343

      前言:redis现在可是就业面试的重中之重了,不得不深度了解学习一下了。


一、概述

1.什么是Redis?    

     远程字典服务。由C语言编写,支持网络,可基于内存亦可持久化的日志型。提供多种语言的API。也被人们称之为结构化数据。


2.Redis可用来做什么?

      根据官方文档介绍,redis可以用作数据库缓存消息队列


1.内存存储、持久化,内存中数据是断电即失。所以说持久化很重要

2.效率高,用于高速缓存,一秒钟可支持10万次读操作,8万次写操作

3.发布订阅信息,微博推送、微信公众号等

4.地图信息分析,使用geo数据类型,支持经纬度存储,可计算位置等

5.计时器,计数器



3.Redis的特性?

    

  1. 性能极高 – Redis 能读的速度是 110000 次/s,写的速度是 81000 次/s 

  2. 丰富的数据类型 – Redis 支持二进制案例的 Strings, Lists, Hashes,

Sets 及Ordered Sets 数据类型操作。还有一些特殊的数据类型

   3.原子 – Redis 的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,通过 MULTI 和 EXEC指令包起来。redis中的事务不保证原子性

   4.丰富的特性 – Redis 还支持 publish/subscribe, 通知, key 过期等等特性。

    5.持久化存储

    6.可以搭建集群


4.Redis基础知识

  1. redis默认有16个数据库,默认使用第0个。(编号0-15)

  2. 使用select  编号  命令可以切换数据库

  3. dbsize  命令查看当前数据库存储数据的大小

  4. flushdb 命令清空当前数据库

  5. flushall  命令清空所有的数据库

  6. Redis是单进程单线程的。Redis是基于内存操作的,CPU不是Redis的性能瓶颈,Redis的瓶颈是根据内存和网络带宽决定的。那么既然可以使用单线程实现,那就使用单线程。利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销

  7. Redis是单线程的,速度为什么这么快?

    redis将所有的数据全部放在内存中,所以说使用单线程去操作的效率就是最高的。多线程存在CPU上下文切换,对于内存系统来说,没有上下文切换的效率就是最高的。多次读写都是在一个CPU上,在内存中这个就是最佳方案



二、数据类型

1.五大基本数据类型

a.字符串(String)

    //常用的API
    set key value 设置键和值
    get key 取值
    append key valuekey中追加值
    strlen key 获取当前字符串的长度


    incr key 当前键对应的值加1
    decr key 当前键对应的值减1
    incrby key 10 当前键对应的值加10
    decrby key 10 当前键对应的值减10


    getrange key 0 3 截取【03】的字符串内容
    setrange key 1 xx 将字符串中下标为1的内容替换成xx


    setex key 5 value (set with expire) #设置过期时间
    setnx (set if not exist) #如果不存在则设置值


    mset k1 v1 k2 v2 k3 v3 批量存储
    mget k1 k2 k3 批量获取


    msetnx k1 v1 k4 v4 #由于k1存在,所以会导致操作失败。因为msetnx是原子操作


    #设置对象
    set user:1 {name:zhangsan,age:3}
    mset user:1:name zhangsan user:1:age 2


    getset key value #先get再set

       String使用场景:字符串中的value值可以是字符串也可以是数字(Integer)

    •    计数器

    •    对象缓存存储


    b.列表(List)

       

      //常用API
      lpush key value #将值插入到列表的头部
      lrange key 0 2 # 获取指定范围的值
      rpush key value #将值插入列表的尾部
      lpop key #从左侧弹出值
      rpop key #从右侧弹出值
      lindex key 1 #通过下标获得list中的某一个值
      llen key #返回列表长度


      lrem key 2 value # 从List移除2个value值


      ltrim key 1 2 # 通过下标截取指定长度,List被改变,只剩下截取的元素


      rpoplpush mylist myotherlist #移除列表的最后一个元素,插入到目标列表的第一个位置


      lset list index value #将list中指定下标位置的值替换


      linsert mylist after "指定指定字符串" "插入字符串"


      List类型一般可当做栈和队列来使用

      c.哈希(Hash)

        hset myhash  field aa    #往hash中放入键值对
        hget myhash field #取hash中的值
        hmset myhash field1 hello field2 world #批量往hash中存储键值对
        hmget myhash field1 field2 #批量获取值
        hgetall myhash #获取hash中所有键值对


        hdel myhash field1 #删除hash指定的key字段
        hlen myhash #获取hash的字段数量
        hexists myhash field1 #判断hash中的字段是否存在


        hkeys myhash #获取所有的字段
        hvals myhash #获取所有的 值


        hincrby myhash field 5 #给hash中的字段增加5
        hincrby myhash filed 5 #给hash中的字段减去5


        hsetnx myhash field4 hello #如果字段不存在则设计值


        #设置对象
        hset user:1 name xuhi

        hash更适合对象的存储,string适合存储字符串


        d.集合(Set)

          //常用API
          sadd key value #往集合中放数据
          smembers key #查看指定set中的元素
          sismember key value #查看当前的value是否在set存在
          scard key #获取set集合中元素个数
          srem key value #移除set中的某一个元素


          srandmember key #随机抽出一个元素
          spop key #随机删除集合中的一些元素


          smove "指定集合" “目标集合” value #将一个指定的值移动到另一个set集合


          sdiff key1 key2 #求两个集合的差集
          sinter key1 key2 #求两个集合的交集
          sunion key1 key2 #求两个集合的并集


          集合中的元素是不可重复的,并且还支持求交集、并集、差集等操作


          e.有序集合(SortedSet)

            //常用API
            zadd k1 score v1 #往set中添加值,set根据score来排序
            zadd myset 2 two 3 three #增加多个值


            zrangebyscore salary -inf +inf #排序
            zrangebyscore salary -inf +inf withscores #显示score排序
            zrevrangebyscore salary -inf +inf #倒叙


            zrange salary 0 -1 #展示set中的值
            zrevrange salary 0 -1 #倒叙展示


            zrem salary xiaohong #移除set中的指定值
            zcard salary #获取set中元素个数


            zcount salary 1 3 获取指定区间的元素个数


            使用场景:一些排序或者排行榜


            2.三大特殊数据类型

            a.geospatial地理位置

              规则:两极无法直接添加,一般会下载城市数据,通过java程序一次性导入
              geoadd china:city 121.47 31.23 shanghai #添加城市的经度、纬度
              geopos china:city beijin #获取指定城市的经度和纬度


              geodist china:city beijin shanghai km #获取城市距离
              #单位 m(米) km (千米) mi (英里) ft(英尺)


              georadius china:city 110.00 30.00 1000km #查找当前经纬度下,半径1000km的城市
              georadius china:city 110.00 30.00 1000km withdist #查找当前经纬度下,半径1000km的城市和直线距离
              georadius china:city 110.00 30.00 1000km withcoord #查找当前经纬度下,半径1000km的城市经纬度


              georadiusbymember china:city beijin 1000km #找出指定城市1000km附近的城市


              #将二维的经纬度转换成一维的字符串
              #返回一个或多个位置的geohash
              geohash china:city beijin chongqin


              zrange china:city 0 -1 #查看当前键下的城市
              zrem china:city beijin #删除指定城市

              使用场景:地图


              b.Hyperloglog

                pfadd mykey a b c  #创建一组元素
                pfcount mykey #统计mykey元素的基数数量
                pf merge mykey3 mykey1 mykey2 # 合并mykey1和mykey2到mykey3

                优点:占用内存固定,2^64个不同的元素只需要12kb

                场景:统计一个网站的访问人数

                传统方式,使用set来存储用户id,然后统计set的长度。这种方式如果保存了大量的用户id就会比较麻烦

                我们的目的是计数而不是保存用户id


                c.Bitmap

                位存储

                  setbit  sign  1  0   #设置值
                  getbit sign 1 #取值
                  bitcount sign #统计这周的打卡记录

                  使用场景:统计打卡,考勤


                  三、事务

                  1.Redsi事务的本质:一组命令的集合,一个事务中所有的命令都会被序列化,在事务执行的过程中,会按照顺序执行

                  2.Redis中的事务具有一致性、顺序性、排他性

                  3.redis单条命令保持原子性,但是事务不保证原子性

                  4.redis中没有隔离级别的概念

                  5.开启事务后,所有的命令在事务中并没有被执行,只有在发起执行命令的时候才会执行


                  redis事务步骤:

                  • 开启事务(multi)

                  • 命令入队

                  • 执行事务(exec)

                  • 取消事务(discard)

                  注意:事务执行的过程中可能存在异常

                  • 编译时异常(命令有错),事务中所有的命令都不会被执行

                  • 运行时异常,如果事务队列中存在语法性,那么执行命令的时候,其他命令可以正常执行



                  监控

                  redis可以实现乐观锁机制


                  悲观锁:类似于人的悲观性格,认为什么时候都会出现问题,无论做什么都会加锁


                  乐观锁:

                  • 认为什么时候都不会出现问题,所有不会加锁,更新数据的时候判断一下,在此期间是否有人修改过这个数据

                  • 获取version

                  • 更新时比较version


                  如何实现乐观锁呢?

                  使用watch命令监控就可以了

                    watch money  实现监控
                    unwatch #放弃监视


                    #如果事务执行失败,就先解锁
                    # 获取最新的值,再次监视
                    #如果没有发生变化,执行成功。否则,执行失败



                    四、Redis持久化 

                           redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一但服务器进程退出,服务器中数据库的状态也会消失,所以redis提供了持久化功能。这里redis提供了两种持久化机制


                    1.RDB

                    redis默认使用的是rdb持久化机制,保存在dump.rdb文件中


                    触发机制

                    1.save的规则满足的情况下,会自动触发rdb规则

                    2.执行flushall命令,也会触发我们的rdb规则

                    3.退出redis,也会产生rdb文件



                    如何恢复rdb中的数据文件

                    1.只需要将rdb文件放在redis的启动目录即可(即配置文件中配置的路径),redis启动的时候会自动检查dump.rdb恢复其中的数据

                    2.查看文件需要存在的位置,使用命令config get dir


                    优点:

                    1.适合大规模的数据恢复

                    2.对数据完整性要求不高

                    缺点:

                    1.需要一定的时间间隔进行操作,如果redis意外宕机了,那么最后一次修改的数据就没有了

                    2.fork进程的时候会占用一定的内存空间


                    2.AOF

                         redis中默认没有开启aof机制,需要手动配置开启,然后重启redis生效。它会将所有的执行过的命令记录下来(只记录写操作,不记录读操作),保存在appendonly.aof文件中,如果aof文件中有错位,那么这时候redis是启动不起来的,我们需要修复这个aof文件。redis提供了一个工具  redis-check-aof --fix


                    优点:

                    1.每一次修改都同步,文件的完整性会更好

                    2.每秒同步一次,可能丢失你一秒的数据

                    3.从不同步,效率最高的

                    缺点:

                    1.相对于数据文件来说,aof远远大于rdb,修复的速度也比rdb慢

                    2.aof的运行效率也比rdb慢

                    如果aof文件大于64M,会触发重写



                    五、Redis发布订阅

                    Redis发布订阅(oub/sub)是一种消息通信的模式,发送者(pub)发送消息,订阅者(sub)接收消息Redis客户端可以订阅任意数量的频道


                      subscrible  mychanel   #订阅一个频道
                      publish mychanel message #在频道上发布一个消息


                      使用场景:

                      • 实时消息系统

                      • 群聊天室

                      复杂的场景用消息中间件来做


                      六、Redis主从复制

                           主从复制,是指一台redis服务器的数据,复制到其他redis服务器,前者称为主节点,后者称为从节点。数据的复制是单向的,只能由主节点到从节点。主节点以写为主,从节点以读为主


                            默认情况下,每台redis服务器都是主节点,且一个主节点可以有多个从节点,或者没有节点,但一个从节点只能有一个主节点


                      主从复制作用:

                      1.数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余的方式

                      2.故障恢复:当主节点出现问题时,可以有从节点提供服务,实现快速的故障恢复,实际上是一种服务的冗余

                      3.负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载,尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高redis服务器的并发量

                      4.高可用基石:主从复制还是哨兵模式和集群能够实施的基础,因此说是redsi高可用的基石


                      主从复制,读写分离。80%都在进行读操作,减轻服务器的压力


                      默认情况下,每台redis服务器都是主节点

                      slaveof 127.0.0.1 6379   #认老大,本机成为从机


                      配置好主从关系后

                      1.主机断开连接,从机依旧连接到主机,但是没有写操作,这个时候,如果主机回来了,从机依旧可以直接获取到主机写入的数据

                      2.如果使用命令行配置的主从关系。这个时候如果从机重启了,主从关系就失效了。重写配置后立马就会从主机中取值


                      七、哨兵模式

                            哨兵模式是一种特殊的模式,首先redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行,原理是哨兵通过发送命令,等待redis服务器响应,从而监控多个redis实例

                            假设服务器宕机了,哨兵1首先检测到这个结果,系统并不会马上进行failover(故障转移)过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象称之为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票是由一个哨兵发起的,进行failover(故障转移)操作,切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器切换成主机,这个过程称之为客观下线。


                      如果master主机断开了,就会投票选择一个从机作为主机。如果主机回来了,这个时候它只能当做从机归并到新的主机下。这就是哨兵模式的规则


                      优点:

                      1.哨兵集群,基于主从复制模式,所有的主从配置优点,它全有

                      2.主从可以切换,故障可以转移,系统的可用性会更好

                      3.哨兵模式是主从模式的升级,手动到自动,更加健壮

                      缺点:

                      redis不好在线扩容,集群容量一但达到上限,在线扩容十分麻烦

                      实现哨兵模式的配置是十分麻烦的




                      八、缓存相关问题

                      1.缓存穿透(查不到数据)

                              缓存穿透是指,用户想要查询一个数据,发现redis内存数据库中没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求持久层数据库,这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透


                      解决方式:

                      1.布隆过滤器

                            布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力


                      2.缓存空对象

                         当存储层不命中后,即使返回的空对象也将其缓存起来,同时设置一个过期时间,之后再访问这个数据会从缓存中获取,保护了后端的数据源


                      但这种方式会存在两个问题

                      • 如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多空值的键

                      • 即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间的不一致,这对于需要保持一致性的业务会有影响


                      2.缓存击穿(热点数据过期)

                          缓存击穿,是指一个key非常的热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求在数据库上,就像在屏障上面开了一个洞

                           当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新的数据,并且写会缓存,会导致数据库瞬间压力过大


                      解决方式:

                      1.设置热点数据永不过期

                           从缓存层面来看,没有设置过期时间,就不会产生这个问题


                      2.加互斥锁

                           分布式锁,使用分布式锁,保证对于每个Key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此需要等待。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大


                      3.缓存雪崩

                          缓存雪崩,是指在某一个时间段,缓存集中过期失效,redis宕机产生一系列的连锁反应,造成整个系统崩溃


                      举例说明:

                          双十一零点时,会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一小时,那么到了凌晨一点,这波商品的缓存就过期了,而对这波商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰,所有的请求都会达到存储层,存储层的调用量就会暴增,会造成存储层垮掉的情况


                      4.缓存预热

                            

                      缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!


                      解决思路:

                      1、直接写个缓存刷新页面,上线时手工操作下;

                      2、数据量不大,可以在项目启动的时候自动进行加载;

                      3、定时刷新缓存


                      5.缓存更新

                      除了缓存服务器自带的缓存失效策略之外(Redis默认的有6种策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:

                      (1)定时去清理过期的缓存;

                      (2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。


                      两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较

                      复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡


                      6.缓存降级

                      当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。

                      降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。

                      以参考日志级别设置预案:

                      (1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;

                      (2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;

                      (3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;

                      (4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户


                      举例:双十一你购买东西后,可能完成不了退货的操作,需等待到第二天,是因为他将退货的服务停掉了或者降级了。将服务的中心转移的秒杀商品上了。





                      文章转载自lamdaxu,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                      评论