• Redis(二)——Redis进阶


    iwehdio的博客园:https://www.cnblogs.com/iwehdio/

    1、redis事务

    • redis事务:

      • 在redis中单条命令具有原子性,但redis事务不保证原子性不存在要么同时成功、要么同时失败。
      • 事务的本质:一组命令的集合。一个事务中所有命令都会被序列化(队列),按照顺序执行。一次性、顺序性、排他性。
      • redis事务没有隔离级别的概念。所有的命令在事务中并没有直接被执行(在入队的时候),在执行命令的时候才会执行。
      • redis事务的三个阶段:
        • 开启事务,multi。
        • 命令入队,一系列的命令...。
        • 执行事务,exec。
      • 放弃事务:discard,此时事务队列中的命令都不会被执行。
      • 代码异常:
        • 写命令出错(类似编译期异常):入队时就会报错,事务中的所有命令都不会被执行。
        • 语法错误(类似运行时异常):执行该命令会抛出异常,但是事务中的其他命令都会执行。
    • redis监控:

      • 悲观锁:认为什么时候都会出问题,无论做什么都会加锁。
      • 乐观锁:认为什么时候都不会出问题,所以不会上锁。更新数据时,判断一下在其期间是否有人修改这个数据。
      • 添加监视:watch 键名。一次监视只在一次事务期间有效,事务结束后要放弃监视。
      • 放弃监视:unwatch
      • redis实现乐观锁:
        • 对一个键添加监控,主线程在事务中正在对该键进行操作。
        • 如果另一个线程修改了这个键,此时事务就会执行失败。
        • 实现的原理:
          • 在开启监视时,获取该键当前的值。
          • 在执行事务时,比对该键的当前值与开启监控时的值是否相同。
          • 如果两个值相同则执行成功,如果发生变化则执行失败。

    2、redis集成

    • Jedis:

      • 使用Java操作redis的中间件。

      • 导入依赖:

        <dependencies>
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>3.3.0</version>
            </dependency>
        </dependencies>
        
      • 连接redis:

        public static void main(String[] args) {
            //直接创建对象
            Jedis jedis = new Jedis("127.0.0.1",6379);
            System.out.println(jedis.ping());
            jedis.close();
        }
        
      • 其他方法大多是jedis.原生方法

    • 整合到SpringBoot:

      • 导入依赖:spring-boot-starter-data-redis。

      • SpringBoot2.x后,连接redis原来使用的jedis被替换为了lettuce。

        • jedis采用的直连,多个线程操作是不安全的,需要使用jedis pool连接池。
        • lettuce采用netty,实例可以在多个线程中共享,不存在线程不安全的情况。
      • 配置文件:

        spring.redis.host=127.0.0.1
        spring.redis.port=6379
        spring.redis.database=0
        
      • 进行操作:

        • 注入redisTemplate。
        • redisTemplate.opsForValue().方法:操作字符串。
        • redisTemplate.opsFor类型().方法:操作不同类型。
        • redisTemplate.方法:操作常用方法。
        • redisTemplate.getConnectionFactory().getConnection():获取连接。
        @Autowired
        private RedisTemplate redisTemplate;
        @Test
        void contextLoads() {
            redisTemplate.opsForValue().set("mykey","iwehdio");
            System.out.println(redisTemplate.opsForValue().get("mykey"));
        
        }
        
      • 自定义RedisTemplate:

        • 如果传入的值是一个对象,这个对象需要被序列化。

        • 创建JSON序列化对象并在模板中设置:

        @Configuration
        public class RedisConfig {
            @Bean
            public RedisTemplate<Object, Object> redisJsonTemplate(RedisConnectionFactory redisConnectionFactory)
                    throws UnknownHostException {
                RedisTemplate<Object, Object> template = new RedisTemplate<>();
                template.setConnectionFactory(redisConnectionFactory);
                Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
                template.setDefaultSerializer(jsonRedisSerializer);
                return template;
            }
        }
        
        • 为了简化开发,可以将RedisTemplate的基本操作封装到自己的工具类中。

    3、redis配置文件

    • 在启动时,就需要配置文件启动。
    • 配置文件的unit单位对大小写不敏感。
    • INCLUDES引入部分,可以将其他配置文件引入。
    • NETWORK网络部分:
      • bind 127.0.0.1是指绑定的ip。
      • protected-mode yes表示时保护模式。
      • port 6379端口设置。
    • GENERAL通用部分:
      • daemonize yes表示应该设置为守护线程(后台)运行。
      • pidfile /var/run/redis.pid如果以后台方式运行,需要指定一个pid文件。
      • loglevel notice日志级别为生产环境。
      • logfile ""日志的文件位置名。
      • databases 16默认的数据库数量。
    • SNAPSHOTTING快照部分:
      • 与持久化有关,在规定的时间内执行的多少操作,则会持久化到文件。
      • save 900 1表示900秒内至少有一个key进行了修改,就进行持久化操作。
      • stop-writes-on-bgsave-error yes表示持久化出错是否还需要继续工作。
      • rdbcompression yes表示是否压缩rdb文件。
      • rdbchecksum yes表示对rdb文集进行错误校验。
      • dir ./表示rdb文件保存的目录。
    • REPLICATION复制部分。
    • SECURITY安全部分:
      • requirepass 密码 表示连接密码。
      • 在命令行使用config set required 密码
      • 登陆:auth 密码
    • CLIENTS客户端部分:
      • maxclients 10000表示最大连接的客户端数量。
      • maxmemory 表示配置的最大内存。
      • maxmemory-policy noeviction表示内存达到上限之后的处理策略。
    • APPEND ONLY MODE aof持久化部分:
      • appendonly no表示默认不开启aof模式,使用rdb方式持久化。
      • appendfilename "appendonly.aof"表示持久化的文件名。
      • appendfsync everysec表示每秒执行一次同步。

    4、redis持久化

    • redis是内存数据库,不保存到磁盘就会断电即失。

    • RDB(Redis DataBase)操作:

      • 在指定的时间间隔内将内存中的数据快照写入磁盘。
      • RDB操作下,redis会单独创建(fork)一个子进程来进行持久化,先将数据写入到一个临时文件中,待持久化过程结束,再用临时文件替换上次持久化好的文件。
      • 在整个过程中,主进程不进行任何的IO操作,保证了高性能。
      • 触发机制:
        • 配置文件中的save规则满足。
        • 执行flushall命令。
        • 退出redis。
      • 恢复rdb文件:把文件放在redis启动目录下。生产环境下需要对rdb文件进行备份。
      • 优点:
        • 适合大规模的数据恢复。
        • 对数据的完整性要求不高。
      • 缺点:
        • 需要一定的时间间隔进行操作。
        • 如果意外宕机,最后一次修改的数据就没有了。
        • 开启子线程时,会占用一定的内存空间。
    • AOF(Append Only File)操作:

      • 将所有的命令都记录下来,恢复的时候就将命令再执行一遍。
      • 以日志的形式记录每个写操作,只许追加文件不可以改写文件。
      • 默认不开启,需要在配置文件中开启。
      • 如果aof文件有错误,redis是启动不起来的,需要用redisredis-check-aof进行修复。
      • 优点:可以设置为每一次修改都同步,提高文件的完整性。
      • 缺点:
        • aof数据文件的大小远大于rdb。
        • 由于是IO操作,运行效率较低。
    • 同时开启两种持久化方式,会默认先载入aof。

    5、redis发布订阅

    • redis发布订阅(pub/sub)是一种消息通信模式,发布者发送消息,订阅者接收消息。

    • redis客户端可以订阅任意数量的频道。

    • 发布订阅消息图:

    • 订阅发布命令:

      • 订阅频道:subscribe 频道名
      • 发布消息:publish 频道名 信息
    • 发布订阅原理:

      • 通过subscribe订阅某个频道后,redis-server中维护了一个字典,字典的键就是频道名。
      • 字典的值是一个链表, 链表中保存了所有订阅这个频道的客户端。subscribe就是将客户端添加到给定频道的订阅链表中。
      • 通过publish发布消息,redis-server会获得对应频道中的链表,遍历链表发送消息。
    • 发布订阅使用场景:

      • 实时消息系统。
      • 实时聊天,将频道作为聊天室。
      • 订阅关注系统。

    6、redis主从复制

    • 主从复制:就是将一台redis服务器的数据,复制到其他的redis服务器。前者称为主节点(master),后者称为从节点(slave)。

    • 数据的复制是单向的,只能由主节点到从节点。主节点以写为主,从节点以读为主。可以减缓服务器的分离。

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

    • 主从复制的主要作用:

      • 数据冗余,实现了数据的热备份。
      • 故障恢复,主节点出问题可以由子节点提供服务,是服务的冗余。
      • 负载均衡:配合读写分离,多个从节点分担读负载。
      • 高可用基石:是哨兵模式和集群能够实施的基础。
    • 在真实的项目中,为了避免宕机和性能瓶颈,不会使用单台redis服务器。

    • 主从复制环境配置:

      • 复制并修改多个配置文件。
      • 按配置文件启动服务器:redis-server 配置文件
      • 按端口启动客户端:redis-cli 端口
      • 更改不同的端口。
      • 更改pidfile、logfile和dvfilename。
      • 在从机中使用命令slaveof 主机ip 主机端口号指定主机。也可以在配置文件中配置。
      • 如果设置slaveof no one则指定该从机为主机。
      • 查看主从机info replication
    • 主从复制细节:

      • 主机可以写,从机只能读。
      • 主机中的所有信息,都会自动被从机保存。
      • 如果主机宕机,从机默认配置的主机不变。在主机重启后,会重新连接。
      • 如果从机宕机,在此宕机期间主机存入的值,该从机仍然可以拿到(如果是命令行设置的从机,重启后该从机变为默认的主机,需要再次设置为从机)。
    • 主从复制原理:

      • slave启动成功连接到master后会发送一个sync同步命令。
      • master接到该命令后,启动后台的存盘进程,同时收集所用接收到的修改数据的命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,完成一次完全同步。
      • 全量复制:slave在接收到数据文件后,将其存盘并加载到内存中。
      • 增量复制:master继续将新的所有收集到的修改命令依此传给slave。
      • 只要有slave重新连接到master,一次完全同步即全量复制就会被自动执行。
    • 主从复制的模式:

      • 一主二从。
      • 层层链路,一主一从,同时第二台从机指定为第一台从机的子节点。第一台从机不可以写入。
      • 哨兵模式。
    • 哨兵模式:

      • 自动选举主机的模式。当主机宕机后,自动将一台从机切换为主机。
      • 哨兵模式能够在后台监控主机是否故障,如果故障了根据投票数自动将从机转换为主机。
      • 哨兵是一个独立的进程。哨兵通过发送命令,等待redis服务器响应,从而监控运行的多个redis实例。
      • 当哨兵监测到master宕机,会自动将一个slave切换为master,并且通过发布订阅模式通知其他slave切换主机。
      • 一个哨兵进程也有可能出问题,可以使用多个哨兵。哨兵不但监控redis,同时也会互相监控,形成多哨兵模式。
      • 如果主机宕机,哨兵1最先检测到了,但是系统并不会马上进行failover(故障转移)操作,仅仅是哨兵1主观的认为主机不可用,这个现象被称为主观下线。
      • 当后边的哨兵也检测到主机不可用,并且数量达到一定值时,那么哨兵之间会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。切换成功后,让各个哨兵把自己监控的从服务器实现切换主机,这个现象被称为客观下线。
    • 哨兵模式配置:

      • 配置哨兵配置文件sentinel.conf:

        #sentinel monitor 被监控的名称 主机ip 主机端口 投票规则(客观下线需要几个哨兵同意)
        sentinel monitor myredis 127.0.0.1 6379 1
        
      • 启动哨兵:

        #windows下
        redis-server sentinel.conf --sentinel
        #linux下
        redis-sentinel sentinel.conf
        
      • 如果主机宕机了 ,从从机中选取一个切换为主机(有一个投票算法)。

      • 如果主机重新连接了,只能归并到新的主机下,作为从机。

    • 哨兵模式的优缺点:

      • 优点:
        • 哨兵集群基于主从复制模式,具有主从复制的优点。
        • 主从可以切换,故障可以转移,系统的可用性更好。
      • 缺点:
        • redis不好在线扩容,集群容量一旦达到上限,在线扩容十分麻烦。
        • 实现哨兵模式的配置其实是很复杂的,里面有很多选择。

    7、redis缓存穿透和雪崩

    • 缓存穿透:
      • 用户想要查询一个数据,发现redis中没有,也就是缓存没有命中,于是向持久层数据库查询,发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,会给持久层数据库带来很大压力。
      • 解决方案:
        • 布隆过滤器。布隆过滤器是一种数据结构,对所有可能查询的参数以hash的形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力。
        • 缓存空对象,在redis中存入这个数据的空对象。当存储层不命中后,即使返回的空对象也将其缓存起来,同时设置一个过期时间,之后在访问这个数据将会从缓存中获取。
        • 缓存空对象的问题:导致缓存需要更多的空间;会导致过期时间内缓存层和存储层的数据一致性问题。
    • 缓存击穿:
      • 指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库。
      • 解决方案:
        • 热点数据永不过期。不设置过期时间,所以不会出现热点key过期的问题。
        • 加互斥锁,使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,只需等待即可。这种方法将高并发的压力转移到了分布式锁。
    • 缓存雪崩:
      • 指在一个时间段,缓存集中过期失效或者服务器宕机。会导致持久层数据库的周期性的压力波峰。
      • 解决方案:
        • redis高可用,搭建集群。
        • 限流降级。在缓存失效后,通过加锁或者队列控制读数据库写缓存的线程数量。
        • 数据预热。在正式部署之间,先把可能的数据先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

    iwehdio的博客园:https://www.cnblogs.com/iwehdio/
    来源与结束于否定之否定。
  • 相关阅读:
    Android 设置图片 Bitmap任意透明度
    Android 设置图片透明度
    Android 设置alpha值来制作透明与渐变效果的实例
    Android 仿微信朋友圈点击图片变暗
    Android 关于录音文件的编解码 实现米聊 微信一类的录音上传的功能
    Android 三种方式实现自定义圆形进度条ProgressBar
    nslookup命令
    netstat命令
    tracert与pathping
    ping命令
  • 原文地址:https://www.cnblogs.com/iwehdio/p/13592091.html
Copyright © 2020-2023  润新知