一些redis的学习笔记,供以后浏览

redis

概念 redis以key-value形式进行存储的NoSql数据库,

  • 使用c语言编写

  • Redis的数据存在内存中,作为缓存工具,读写性能好

  • 存取数据

    • 对key进行CRC16算法,得到结果后取16384,之后放入对应的槽中
  • 通过Redis Sentinel(哨兵)提供高可用,通过Redis Cluster(集群)提供自动分区

Redis作为缓存工具写代码的流程(边路缓存思想中的一部分)

  • 为什么快,是服务器端的速度快了,请求及响应的时间少了
  • 什么是边路缓存(一种思想)
      1. 查询的时候先去查询缓存,不存在就去查数据库。
      1. 修改数据的时候,先修改数据库,后修改缓存

数据查询的流程图

  • 查询key->在不在Redis缓存中,key是否存在,key存在返回value,key不存在去查询sql数据库,数据库返回给主程序,再将数据缓存进主函数。
    修改的时候先去修改数据库,再去修改redis
    search(String a) => bool
    if true => value Map.get(key)
    else => value SQL.get(key) => redis.save(key:value)

Redis的单机模式下的一些命令

  • String 类型

    • set 修改 覆盖 设置过期时间EX/PX 10
    • get 查询
    • setnx 没有的话新增,有的话不增
  • Hash

    • Hash类型的值,key中包含了多组field value 类似于对象
    • key :
      • field - value
      • field - value
      • field - value
    • 取出单个
      • hset [key] [field] [value]
      • hget [key] [field] [value]
      • hset people name "kobe"
    • 取出多个:
      • hmset [key] [field1] [value] [field2] [value]
      • hmget [key] [field1] [field2]
    • 取出所有key下的field : value对
      • hvals [key]
      • 结果 field
      • value
    • 删除掉其中一个field,del删除的是所有的
      • hdel [key] [field]
  • List 类似与双端队列

    • key :
      - value1
      - value2
      - value3
    • 在头部加,尾部加
      • Rpush list "b" "c"(重复两次 rpush)
      • Rpush list a
      • Lpush
    • 取前几项
      • lrange list 0 3
        • 0 -1(末尾)
      • llen list
    • 从左往右查,删掉两个1
      • lrem [list名] [从左往右的数量] [value]
      • lrem list 2 "1"
  • set 集合

    • sadd key 1 2 3

    • 查询所有

    • smembers set

    • 查询总数量

    • scard set(key)

    • 集合不允许重复,sadd依旧是空

    • set是无序的

  • sortedset 有序集合

    • 每个value都有分数
    • zadd [key] [value] [score]
    • zadd set
    • zrange set 0 -1 区间从小到大
  • redis 持久化策略

    • Redis每次启动都将数据从磁盘读到内存

    • 有两种持久化策略 RDB(Redis DataBase) AOF (AppendOnly File往文件中追加)

    • RDB

      • RDB在指定时间间隔内生成数据快照 默认保存到dump.rdb的文件中
      • 用户使用save(同步 阻塞线程)(配置文件 save 每900秒 1数据操作,进行记录 save 300 1 save 60 10000) / BGSave(异步)< = 手动保存数据
      • 使用RDB性能会高于AOF,恢复数据的效率会高于AOF
      • 出乎意料的关闭或者保存点之间数据的关闭,会导致丢失数据。
      • 每次保存,都会fork子进程,会比较消耗性能
    • AOF

      • 默认不生效,生效后的优先级大于RDB
      • 出现增删改查操作(每次的结果记录到日志中)之后,会同步到数据库之中
      • 出现问题使用AOF日志
      • AOF与硬盘进行交互,影响了性能,稍微慢一点RDB
      • 安全
      • 相同数据集AOF文件大于RDB,AOF存命令,RDB存数据
    • appendonly no

    • appendfilename ""文件名


使用docker建立redis的主从复制、哨兵模式、集群模式方法

  • 基于docker的主从复制的建立过程

    • 主从

      • 读写分离
      • master 写,slave读
    • 创建三个redis

      • docker pull redis
    • 创建 redis 使用本机端口=> 映射到本机端口出口 使用版本 /

      • docker run --name redis1 6379:6379 -v /opt/redis:/data -d redis
      • docker run --name redis2 6380:6379 -v /opt/redis:/data -d redis
      • docker run --name redis3 6381:6379 -v /opt/redis:/data -d redis
    • 运行redis1 中的redis-cli
      sudo docker exec -it redis1 redis-cli

    • 使用role命令,查询docker redis的角色

    • 使用slaveof ip port设定从节点

    • docker inspect redisname 查询到docker下的网络ip从而找到与主节点master的连接

    • 主从模式下,主节点具备写的能力,从节点不具备

  • 哨兵模式

    • 监督每一个主从节点的状态,当master节点出现崩溃,他会从中选择一个主出来。
    • 单哨兵,多哨兵
    • 1个和多个 多个需要投票 人为在配置文件中,设定当多少个redis认定错误的时候master会将其认定为master出现异常,进行替换
  • redis脑裂

    • 当出现网络波动时,master可能被哨兵认为是错误的,会被哨兵投票替换掉,从从节点中选择一个当作主节点,但是当网络恢复时,本来已经宕机后的节点恢复,在节点中出现了两个master的时候被称为脑裂现象
    • 解决方法 配置文件中修改
      • min-slave-to-write 3 // 连接到master的最小 slave数量(当master有一个,而只有三个slave的时候,设置3个最小slave数量表示master发生网络波动将不会影响其他的slave)
      • min-slave-max-lag 10 //slave 连接到master的最大延迟时间
  • 搭建高性能集群

    • 集群搭建完成之后由集群节点平分16384个槽。(不能平分时,前几个节点多一个槽)
    • 客户端可以访问集群中的任意一个几个点
    • 集群新增或者查询一个键值对时,会对key进行crc16算法,得到一个小于16384的值,然后去操作相应的节点。
    • 当集群中的节点超过1/2不可用时,整个集群不可用,为了搭建稳定集群,都采用奇数节点。
    • 哨兵会监控主状态,如果出现了问题(在配置文件中确定有多少个节点出现了问题),会进行投票,投票选择一个从当做主,如果后期恢复了,主当做从加入节点,在搭建redis时,内置哨兵策略。
  • 集群创建 :

    • 配置文件
        port ${PORT}
        cluster-enabled yes
        cluster-config-file nodes.conf
        cluster-node-timeout 5000
        cluster-announce-ip 192.168.133.3  
        cluster-announce-port ${PORT}
        cluster-announce-bus-port 1${PORT}
        appendonly yes
        // 设置的是自己的本机
    
    • 集群的shell脚本,在文件夹下创建6个文件夹
        for port in `seq 7000 7005`;do 
        mkdir -p ./${port}/conf 
        && PORT=${port} envsubst < ./redis-cluster.tmpl > 
        ./${port}/conf/redis.conf  
        && mkdir -p ./${port}/data; 
        done
    
    
    • 建立集群
         for port in $(seq 7000 7005); 
    do 
        docker run -it -d -p ${port}:${port} -p 1${port}:1${port} 
        --privileged=true -v /usr/local/redis-cluster/${port}/conf/redis.conf:/usr/local/etc/redis/redis.conf 
        --privileged=true -v /usr/local/redis-cluster/${port}/data:/data 
        --restart always --name redis-${port} --net redis-net 
        --sysctl net.core.somaxconn=1024 redis redis-server /usr/local/etc/redis/redis.conf; 
    done
    
    
    • 执行集群脚本
        redis-cli --cluster create 
        192.168.133.3:7000 
        192.168.133.3:7001 
        192.168.133.3:7002 
        192.168.133.3:7003 
        192.168.133.3:7004 
        192.168.133.3:7005 
        --cluster-replicas 1
        设置一主一从,记得关闭防火墙 sudo ufw disable
    
    • 运行错误,清空docker内容命令
    docker stop redis-7000 redis-7001 redis-7002 redis-7003 redis-7004 redis-7005
    docker rm redis-7000 redis-7001 redis-7002 redis-7003 redis-7004 redis-7005
    rm -r 7000 7001 7002 7003 7004 7005
    
    • 测试,以集群方式-c -p是端口号
    redis-cli -c -h 192.168.133.3 -p 7000

spring data redis

  • 设计模式:模板方法 .template

  • 启动器springbootshartdataredis

  • 注入xxxtemplate

  • 方法和变量名:

    • opsForValue:String值,如果储存Java对象或Java中集合时就需要使用序列化器,进行序列化成json字符串
    • opsForList:列表
    • opsForHash: 哈希表
    • opsForZset: 有序集合 sorted Set
    • opsForSet: 集合
  • 序列化器(什么是序列化,配置文件@Config怎么写,注解@Bean)

    • 序列化器jdkSerializationRedisSerializer

    • String序列化器

    • genericToStringSerializer

      • 需要调用者给传递一个对象到字符串互转的Converter(转换器),比较麻烦
    • OxmSerialization序列化器(无人用)

      • 使用xml进行序列化存储
      • 要自定义xml文件流复杂
    • Jackson2JsonRedisSerializer

      • 转换成了json字符串
      • 无法转回来,不能使用强转回,要自己
    • GenericJackson2JsonRedisSerializer

      • 和jdk的使用相同,但是可以显示中文在redis-manager中,可读性好
      • 增加了一个类名信息
    • Spring Cache使用了jdk序列化器,更加方便

    • 注:中文字符串使用redis-manager

  • 步骤

    • 添加依赖springdata start redis
    • 配置配置文件
    • 编写模板类
    • 编写代码
      • set get
      • del exist等

redis缓存的问题

  • 缓存穿透(key 不存在,多为null)
    • 实际情况下,添加缓存工具的目的是,减少对数据库的访问,增加效率。肯定会出现redis中不存在缓存数据的情况。例如:id=-1,不存在redis中,会访问数据库,在高并发的情况下,redis依旧频繁访问数据库就叫做缓存穿透,穿透的是redis,因为redis没有,要去频繁访问数据库。出现情况,多是数据库中没有,查询结果多为null的时候,不被缓存
    • 解决方法:
      • 查询的结果即使为null,依旧缓存到redis中。
      • 设置key的ex也就是存在时间比其他的内容短一些
      • 代码
        if(list == null){
            redisTemplate.opsForValue().set(listName,10,TimeUnit.MINUTES);
        }
        else{
            redisTemplate.opsForValue().set(listName,10,TimeUnit.DAYS);
        }
  • 缓存击穿(单一的key 存在,但是过期了,有大量的访问都在这个key)

    • 考虑redis存放数据的内存压力,都会设置key的有效时间,会出现键值对过期。当键值对刚刚过期的时候,同一时刻,突然有大量的key去访问刚刚过期的键值对,导致都要去访问数据库,造成缓存击穿
    • 解决方法
      • 永久数据(懒)内存压力大
      • 加锁。防止数据库的并发访问,单列锁的概念
    • 锁的概念synchronize锁和lock锁,ReentrantLock锁(重入锁): 按需开锁就是乐观锁,什么也不考虑就是悲观锁: 就是在在锁前用if判断,而上来就用锁,属于悲观锁
      • synchronize
        • 可以加到方法上
        • 不需要自己解锁
        • 也可以使用代码块
              synchronize(this){
                  DATABASE.SELECT();
              }
          
      • lock锁,需要手动的开锁和解锁
      • concurent锁 重入锁
          ReentrantLock lock = new ReentrantLock();
          lock.lock();
          if(lock.islock()){
              System.out.println("run");
          }
          thread
          具体代码见IDEA
      
  • 缓存雪崩(大量数据失效,key访问都要去找数据库)

    • 不同于单一key的失效,由于设置的ex可能在短时间内,同时发生失效的可能,导致访问redis的数据时不存在,要大量的访问在数据库的数据,这样一来就发生了缓存雪崩
    • 解决方法
      • 永久生效
      • 自定义算法 随即有效时间
        • 一段时间内出现大量的请求是高并发,10000秒以上就不是高并发了
        • 代码,设置一个10000的随机数,并设置ex为100+随机数,这样就应付了高并发
            int seconds = random.nextInt(10000);
            redisTemplate.opsValue().set(key,item,100 + seconds, TimeUnit.SECOND);
        
        
  • Redis缓存淘汰策略/内存不足如何回收/redis如何进行缓存淘汰策略

    • Redis数据放入内存中,内存占用会越来越大,最终导致内存溢出
    • Redis内置了缓存淘汰策略,使用在配置文件中
      • maxmemory-policy noeviction 缓存最大的阈值 为maxmemorynoeviction默认策略 默认不删除key,超过时报错
      • maxmemory-policy 最大的缓存占用大小
      • 其他设置
        • volateile-lru从其他设置过期的key中操作,选择使用数量最少的
        • allkeys-lru 从所有key中,选择使用次数最少的
        • volateile-lfu 对有过期时间的key进行lfu算法
        • allkey-lfu 对所有key进行lfu算法
        • volateile-random 对有过期时间的key进行随机删除
        • allkey-random 对所有的key进行随机删除
        • volateile-ttl 在有过期时间的key中删除过期时间最短的key
        • noeviction 对超过了缓存最大的阈值,就报错
  • 操作系统相关

  • LRU Least Recently Used 最近未使用算法

    • 链表,新增放入链表头
    • 修改了哪个数据就放到链表头
    • 到达了阈值,删除最后一个数据
  • LFU Least Frequent Used 最近最不经常使用(某一时间段)

    • 计数器,新增就计数
    • 删除时,最后删除数据访问次数最小的
  • 何时淘汰数据

原文地址:https://www.cnblogs.com/kobe961231/p/14535564.html