zoukankan      html  css  js  c++  java
  • 一些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 最近最不经常使用(某一时间段)

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

  • 相关阅读:
    IE下CSS属性float:right下换行问题解决方法
    php 中简单输出 csv和excel
    VMware 链接网络的三种模式及自己的安装方法
    ajax的应用
    php中ADODB的用法
    关于web 标准的常见问题 总结
    javascript 闭包
    php strrev 中文字符串翻转乱码的问题
    注册表 一览
    SVN Commit报错 svn: E155037: Previous operation has not finished; run 'cleanup' if it was interrupted
  • 原文地址:https://www.cnblogs.com/kobe961231/p/14535564.html
Copyright © 2011-2022 走看看