小哥之哥 小哥之哥
首页
    • Prometheus
    • Kubertenes
    • Docker
    • MySQL
  • Go
  • Python
  • Vue
  • Jenkins
  • ELK
  • LDAP
  • 随笔
  • 最佳实践
  • 博客搭建
  • 问题杂谈
关于
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

小哥之哥

运维扫地僧
首页
    • Prometheus
    • Kubertenes
    • Docker
    • MySQL
  • Go
  • Python
  • Vue
  • Jenkins
  • ELK
  • LDAP
  • 随笔
  • 最佳实践
  • 博客搭建
  • 问题杂谈
关于
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Kubertenes

  • Prometheus

  • Docker

  • 数据库

    • MySQL

    • Redis

      • Redis 数据类型
        • Redis 集群部署
      • MongoDB

    • 运维利器

    • 运维
    • 数据库
    • Redis
    tchua
    2023-02-09
    目录

    Redis 数据类型

    # 一、String(字符串)

    # 1.1 介绍

      string 是 redis 最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。value其实不仅是String,也可以是数字。string 类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象。string 类型的值(value)最大能存储 512MB。
    
    1

    # 1.2 常用命令

    • 赋值

      # 1) 语法
      SET KEY_NAME VALUE
      SET 命令用于设置给定 key 的值。如果 key 已经存储值, SET 就覆写旧值,且无视类型。
      # 2) 示例 
      127.0.0.1:6379> set user tchua  # 赋值
      OK
      127.0.0.1:6379> get user	    # 取值
      "tchua"
      127.0.0.1:6379> set user xiaohua # 覆盖原有值
      OK
      127.0.0.1:6379> get user
      "xiaohua"
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
    • 删除

      127.0.0.1:6379> set user huahua
      OK
      127.0.0.1:6379> get user
      "huahua"
      127.0.0.1:6379> del user
      (integer) 1
      127.0.0.1:6379> get user
      (nil)
      
      1
      2
      3
      4
      5
      6
      7
      8
    • 自增/自减

      # 1 语法
      INCR KEY_Name
      Incr命令将key中储存的数字值增1。如果 key不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR操作.
      1) incr key 递增数字
      2) incrby key increment 增加指定整数
      127.0.0.1:6379> INCRBY id 15
      (integer) 15
      127.0.0.1:6379> INCRBY id 20
      (integer) 35
      3) decrby key decrement 减少指定的整数
      127.0.0.1:6379> DECRBY id 5
      (integer) 30
      127.0.0.1:6379> DECRBY id 10
      (integer) 20
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
    • 批量操作

      # 设置多值
      127.0.0.1:6379> MSET user xiaohua age 10
      OK
      127.0.0.1:6379> get user
      "xiaohua"
      127.0.0.1:6379> get age
      "10"
      # 获取多值
      127.0.0.1:6379> mget user age
      1) "xiaohua"
      2) "10"
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11

    # 1.3 key命名规范

    "对象类型:对象id:对象属性",对于多个单词推荐用.分割。如键user:1:friends表示id为1的用户的好友列表.
    127.0.0.1:6379> set user:1:friends [xiaohua,dahua,tchua]
    OK
    127.0.0.1:6379> get user:1:friends
    "[xiaohua,dahua,tchua]"
    
    1
    2
    3
    4
    5

    # 1.4 应用场景

    缓存功能:字符串最经典的使用场景,redis最为缓存层,Mysql作为储存层,绝大部分请求数据都是
             redis中获取,由于redis具有支撑高并发特性,所以缓存通常能起到加速读写和降低 后端压力的作用。
    计数器:许多运用都会使用redis作为计数的基础工具,他可以实现快速计数、查询缓存的功能,
            同时数据可以一步落地到其他的数据源,使用 INCRBY 完成统计“点击数”,由于单线程,可以避免并发问题。
            如: 微博数, 粉丝数。
    共享session:出于负载均衡的考虑,分布式服务会将用户信息的访问均衡到不同服务器上,
            用户刷新一次访问可能会需要重新登录,为避免这个问题可以用redis将用户session集中管理,
            在这种模式下只要保证redis的高可用和扩展性的,每次获取用户更新或查询登录信息
            都直接从redis中集中获取。
    限速:出于安全考虑,每次进行登录时让用户输入手机验证码,为了短信接口不被频繁访问,
            会限制用户每分钟获取验证码的频率。
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    # 二、Hash(哈希)

    # 2.1 介绍

     Hash可以看成是一个string类型的field和value的映射表,也就是键值对类型。所以适合存储 值对象的信息,如姓名,年龄,生日等等,每一个hash可以存储4294967295个键值对。
    
    1

    # 2.2 常用命令

    • 赋值

      # 1) 赋值
      127.0.0.1:6379> HSET users user:1 xiaohua age:1 18 bir:1 1992-05-25
      (integer) 3
      # 2) 获取
      127.0.0.1:6379> HGET users user:1
      "xiaohua"
      127.0.0.1:6379> HGET users age:1
      "18"
      # 3) 插入新值
      127.0.0.1:6379> HSET users money:1 18000
      (integer) 1
      # 4) 获取全部值
      127.0.0.1:6379> HGETALL users
      1) "user:1"
      2) "xiaohua"
      3) "age:1"
      4) "18"
      5) "bir:1"
      6) "1992-05-25"
      7) "money:1"
      8) "18000"
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
    • 删除

      # 1) 删除具体某一个值
      127.0.0.1:6379> HDEL users user:1
      (integer) 1
      # 2) 删除整个key
      127.0.0.1:6379> del users
      
      1
      2
      3
      4
      5

    # 2.3 应场景

      哈希结构相对于字符串序列化缓存信息更加直观,并且在更新操作上更加便捷。所以常常用于用户信息等管理,但是哈希类型和关系型数据库有所不同,哈希类型是稀疏的,而关系型数据库是完全结构化的,关系型数据库可以做复杂的关系查询,而redis去模拟关系型复杂查询开发困难,维护成本高。
    
    1

    # 三、List(列表)

    # 3.1 介绍

    简单的字符串列表,可以从头部或尾部向redis列表添加元素。
    
    1
    • 特性

      1. 列表中的元素是有序的,这就意味着可以通过下标获取某个元素或者某个范围内的元素列表。
      2. 列表中的元素是可以重复的。
      
      1
      2
    • 数据存储方式

      1. arraylist(数组的方式):所以根据"索引"去"查询"是比较快的,但是"新增"和"删除"比较慢,因为涉及到"位移"操作。
      2. LinkedList方式(双向链表的方式):对每个元素都记录了前后的"指针","插入"和"删除"的时候只是改变元素的"指针"指向即可,所以速度快。
      3. 双向链表添加数据
      4. 双向链表删除数据
      
      1
      2
      3
      4

    # 3.2 常用命令

    • 两端添加

      # lpush 左边添加 (栈) 先进后出
      127.0.0.1:6379> lpush user_list 1 2 3 4
      (integer) 4
      127.0.0.1:6379> LRANGE user_list 0 -1
      1) "4"
      2) "3"
      3) "2"
      4) "1"
      # rpush 右边添加 (队列) 先进先出
      127.0.0.1:6379> rpush ls 1 2 3 4
      (integer) 4
      127.0.0.1:6379> LRANGE ls 0 -1
      1) "1"
      2) "2"
      3) "3"
      4) "4"
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
    • 删除

      # 1) 左边删除
      127.0.0.1:6379> lpush user_list 1 2 3 4
      (integer) 4
      127.0.0.1:6379> LPOP user_list
      "4"
      127.0.0.1:6379> LPOP user_list
      "3"
      127.0.0.1:6379> LPOP user_list
      "2"
      127.0.0.1:6379> LPOP user_list
      "1"
      # 2) 右边删除
      127.0.0.1:6379> lpush user_list 1 2 3 4
      (integer) 4
      127.0.0.1:6379> RPOP user_list
      "1"
      127.0.0.1:6379> RPOP user_list
      "2"
      127.0.0.1:6379> RPOP user_list
      "3"
      127.0.0.1:6379> RPOP user_list
      "4"
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22

    # 3.3 应用场景

    1. twitter 的关注列表、粉丝列表等都可以用 Redis 的 list结构来实现
    2. 抢购:使用redis的原子操作来实现这个“单线程”,首先我们把库存存在good_things:1这个列表中,如果有10件库存,就往列表中push10个数。抢购开始后,每来一个用户,就或从good_things:1中pop一个数,表示抢购成功,列表为空时,表示商品已经被抢完。pop操作是原子性的,此时如果有很多用户同时到达,也是依次执行的。
    
    
    1
    2
    3

    # 3.4 常用技巧

    lpush+lpop=Stack(栈) 
    lpush+rpop=Queue(队列) 
    lpush+ltrim=Capped Collection(有限集合) 
    lpush+brpop=Message Queue(消息队列)
    
    1
    2
    3
    4

    # 四、Set(集合)

    # 4.1 介绍

      集合类型也是用来保存多个字符串的元素,但和列表不同的是集合中不允许有重复的元素,并且集合中的元素是无序的,不能通过索引下标获取元素,redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集,并合理的使用好集合类型,能在实际开发中解决很多实际问题。
    
    1

    # 4.2 常用命令

    # 1) 增加
    127.0.0.1:6379> sadd myset 1 2 3
    (integer) 3
    # 2) 删除
    127.0.0.1:6379> SREM myset 1
    (integer) 1
    # 3) 获得集合中所有元素
    127.0.0.1:6379> SMEMBERS myset
    1) "2"
    2) "3"
    # 4) 判断元素是否存在集合中 0:不存在 1:存在
    127.0.0.1:6379> SISMEMBER myset 1
    (integer) 0
    127.0.0.1:6379> SISMEMBER myset 2
    (integer) 1
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    # 4.3 应用场景

      集合类型比较典型的使用场景,如一个用户对娱乐、体育比较感兴趣,另一个可能对新闻感兴趣,这些兴趣就是标签,有了这些数据就可以得到同一标签的人,以及用户的共同爱好的标签, 这些数据对于用户体验以及曾强用户粘度比较重要。(用户和标签的关系维护应该放在一个事物内执行,防止部分命令失败造成数据不一致)
    
    1

    # 4.4 常用技巧

    sadd=tagging(标签) 
    spop/srandmember=random item(生成随机数,比如抽奖) 
    sadd+sinter=social Graph(社交需求)
    
    1
    2
    3

    # 五、(Sorted Set)有序集合

    # 5.1 介绍

      有序集合和集合有着必然的联系,他保留了集合不能有重复成员的特性,但不同得是,有序集合中的元素是可以排序的,但是它和列表的使用索引下标作为排序依据不同的是,它给每个元素设置一个分数,作为排序的依据。
    
    1

    # 5.2 常用命令

    • 加入元素
    127.0.0.1:6379> zadd my-zset 100 num01
    (integer) 1
    127.0.0.1:6379> zadd my-zset 100 num001
    (integer) 1
    127.0.0.1:6379> zadd my-zset 101 num02
    (integer) 1
    127.0.0.1:6379> zadd my-zset 102 num03
    (integer) 1
    
    1
    2
    3
    4
    5
    6
    7
    8
    • 查看元素

      127.0.0.1:6379> ZRANGE my-zset 0 -1
      1) "num001"
      2) "num01"
      3) "num02"
      4) "num03"
      127.0.0.1:6379> ZRANGE my-zset 0 -1 withscores
      1) "num001"
      2) "100"
      3) "num01"
      4) "100"
      5) "num02"
      6) "101"
      7) "num03"
      8) "102"
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
    • 删除

      127.0.0.1:6379> ZREM my-zset num001
      (integer) 1
      
      1
      2
    • 补充

      zscore my-zset num01 		-- 获得元素的分数
      ZINCRBY my-zset 100 num001   -- 增加某个元素的分数
      ZRANK my-zset num001		-- 获得元素的排名
      
      1
      2
      3

    # 5.3 应用场景

      Redis有序序列的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
      另外还可以用Sorted Sets来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。
    
    1
    2

    # 六、总结

    # 6.1 各数据类型应用场景

    类型 简介 特性 场景
    String(字符串) 二进制安全 可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M
    Hash(字典) 键值对集合,即编程语言中的Map类型 适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值(Memcached中需要取出整个字符串反序列化成对象修改完再序列化存回去) 存储、读取、修改用户属性
    List(列表) 链表(双向链表) 增删快,提供了操作某一段元素的API 1、最新消息排行等功能(比如朋友圈的时间线) 2、消息队列
    Set(集合) 哈希表实现,元素不重复 1、添加、删除、查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作 1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐
    Sorted Set(有序集合) 将Set中的元素增加一个权重参数score,元素按score有序排列 数据插入集合时,已经进行天然排序 1、排行榜 2、带权重的消息队列

    # 6.2 Redis 特性

    1) 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s(官方测试数据)。
    2) 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
    3) 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
    4) 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
    
    1
    2
    3
    4

    # 6.3 公共命令

    1) DEL key
    2) DUMP key:序列化给定key,返回被序列化的值
    3) EXISTS key:检查key是否存在
    4) EXPIRE key second:为key设定过期时间
    5) TTL key:返回key剩余时间
    6) PERSIST key:移除key的过期时间,key将持久保存
    7) KEY pattern:查询所有符号给定模式的key
    8) RANDOM key:随机返回一个key
    9) RANAME key newkey:修改key的名称
    10) MOVE key db:移动key至指定数据库中
    11) TYPE key:返回key所储存的值的类型
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    # 七、补充

    # 7.1 内存淘汰策略

    过期策略

    设置一个key后,指定key的过期时间。
    set name 123
    EXPIRE name 20  -- 将键的生存时间设置为20s
    PEXPIRE name 20 -- 将键的生存时间设置为20毫秒
    EXPIREAT name timestamp --将键的生存时间设置为timestamp 所指定的时间
    TTL name 		-- 查看key剩余的过期时间(s)
    PTTL name		-- 查看key剩余的过期时间(毫秒)
    PERSIST name	-- 移除一个键的过期时间
    
    1
    2
    3
    4
    5
    6
    7
    8
    • 定期删除

        定期删除指的是Redis默认每隔100ms就随机抽取一些设置了过期时间的key,检测这些key是否过期,如果过期了就将其删掉,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。
      
      1
    • 惰性删除

        惰性删除不再是Redis去主动删除,而是在客户端要获取某个key的时候,Redis会先去检测一下这个key是否已经过期,如果没有过期则返回给客户端,如果已经过期了,那么Redis会删除这个key,不会返回给客户端。
        所以惰性删除可以解决一些过期了,但没被定期删除随机抽取到的key。但有些过期的key既没有被随机抽取,也没有被客户端访问,就会一直保留在数据库,占用内存,长期下去可能会导致内存耗尽。所以Redis提供了内存淘汰机制来解决这个问题。
      
      1
      2

    内存淘汰机制

      Redis在使用内存达到某个阈值(通过maxmemory配置)的时候,就会触发内存淘汰机制,选取一些key来删除。内存淘汰有许多策略,下面分别介绍这几种不同的策略。
    # ## 开启参数
    # maxmemory <bytes> 配置内存阈值
    # maxmemory-policy noeviction  -- 配置淘汰机制
    
    1
    2
    3
    4
    • 淘汰机制

      1) noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。默认策略
      2) allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
      3) allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
      4) volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
      5) volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
      6) volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
      
      1
      2
      3
      4
      5
      6
    • LRU

        LRU是Least Recently Used的缩写,即最近最少使用。LRU源于操作系统的一种页面置换算法,选择最近最久未使用的页面予以淘汰。在Redis里,就是选择最近最久未使用的key进行删除。
      
      1

    # 7.2 缓存失效

    • 缓存雪崩

      # 现象
      影响轻则,查询变慢,重则当请求并发更高时,出来大面积服务不可用。
      # 原因
      雪崩就是指缓存中大批量热点数据过期后系统涌入大量查询请求,因为大部分数据在Redis层已经失效,请求渗透到数据库层,大批量请求犹如洪水一般涌入,引起数据库压力造成查询堵塞甚至宕机
      # 解决方案
      将缓存失效时间分散开,比如每个key的过期时间是随机,防止同一时间大量数据过期现象发生,这样不会出现同一时间全部请求都落在数据库层,如果缓存数据库是分布式部署,将热点数据均匀分布在不同Redis和数据库中,有效分担压力,别一个人扛。
      
      1
      2
      3
      4
      5
      6
    • 缓存穿透

      # 现象
        就是指用户不断发起请求的数据,在缓存和DB中都没有,比如DB中的用户ID是自增的,但是用户请求传了-1,或者是一个特别大的数字,这个时候用户很有可能就是一个攻击者,这样的功击会导致DB的压力过大,严重的话就是把DB搞挂了。因为每次都绕开了缓存直接查询DB
      # 解决方案
       1、每次系统 A 从数据库中只要没查到,就写一个空值到缓存里去,比如 set -999 UNKNOWN。然后设置一个过期时间,这样的话,下次有相同的 key 来访问的时候,在缓存失效之前,都可以直接从缓存中取数据。
       2、在接口层增加校验,不合法的参数直接返回。不相信任务调用方,根据自己提供的API接口规范来,作为被调用方,要考虑可能任何的参数传值。
       3、高级用户布隆过滤器(Bloom Filter),这个也能很好地防止缓存穿透。原理就是利用高效的数据结构和算法快速判断出你这个Key是否在DB中存在,不存在你return就好了,存在你就去查了DB刷新KV再return。
      
      1
      2
      3
      4
      5
      6
    • 缓存击穿

      # 现象
      跟缓存雪崩类似,但是又有点不一样。雪崩是因为大面积缓存失效,请求全打到DB;而缓存击穿是指一个key是热点,不停地扛住大并发请求,全都集中访问此key,而当此key失效瞬间,持续的大并发就击穿缓存,全都打在DB上。就又引发雪崩的问题。
      # 解决方案
      1、可以将热点数据设置为永远不过期。
      2、基于redis or zookeeper实现互斥锁,等待第一个请求构建完缓存之后,再释放锁,进而其它请求才能通过该key访问数据。
      
      1
      2
      3
      4
      5

    # 参考文档:

    https://juejin.im/post/5d8882c8f265da03951a325e

    编辑 (opens new window)
    #Redis
    上次更新: 2023/06/09, 09:57:05
    MySQL 5.7 基于组复制(MGR)多主模式
    Redis 集群部署

    ← MySQL 5.7 基于组复制(MGR)多主模式 Redis 集群部署→

    最近更新
    01
    cert-manager自动签发Lets Encrypt
    09-05
    02
    Docker构建多架构镜像
    08-02
    03
    Prometheus数据迁移至VMstorage
    08-01
    更多文章>
    Theme by Vdoing | Copyright © 2023-2024 |豫ICP备2021026650号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式