美文网首页
2019-11-29

2019-11-29

作者: 赤道的飞雪_f6e1 | 来源:发表于2019-11-29 11:00 被阅读0次

    大key如何发现及治理?

    发现

    redis4.0之前

    1. redis-cli --bigkeys命令。可以找到某个实例5种数据类型(String、hash、list、set、zset)的最大key。

    优点是可以在线扫描(用scan命令通过游标遍历键空间并且在生产上可以通过对slave执行该命令),不阻塞服务;缺点是信息较少,内容不够精确。扫描结果中只有string类型是以字节长度为衡量标准的。List、set、zset等都是以元素个数作为衡量标准,元素个数多不能说明占用内存就一定多。

    1. 自定义的扫描脚本,通过strlen、hlen、scard等命令获取字节大小或者元素个数。这样获取的到和上述1缺点一样,不能获取到除string类型外的具体字节长度等内存信息。

    2. debug object key命令。可以查看某个key序列化后的长度,每次只能查找单个key的信息。(元素个数较多的数据结构,debug object执行速度比较慢,存在阻塞Redis的可能,所以不推荐)。

    3. 通过了解rdb文件结果,通过工具解析rdb文件,分析rdb文件,来获取大key,网上的redis-rdb-tools,godis-cli-bigkey等。redis实例上执行bgsave,然后对dump出来的rdb文件进行分析,找到其中的大KEY。

    redis4.0之后

    memory usage key命令

    采用抽样估算整个key的内存大小(比如hash类型有100个field,只抽取5个(默认值)来估算整个的内存)样本的数量决定了key的内存大小的准确性和计算成本,样本越大,循环次数越多,计算结果更精确,性能消耗也越多。可以通过SAMPLES参数设置样本大小

    我们可以通过脚本在集群低峰时扫描Redis,循环去获取所有key的内存大小。

    治理

    redis monitor

    1. 客户端通过执行MONITOR命令可以将自己变为一个监视器,实时地接受并打印出服务器当前处理的命令请求的相关信息,如下图


      image1.png

    此时,当其他客户端向服务器发送一条命令请求时,服务器除了会处理这条命令请求之外,还会将这条命令请求的信息发送给所有监视器:


    image2.png

    2.原理

    • redisServer维护一个monitors的链表,记录自己的监视器,每次收到MONITOR命令之后,只需将客户端追加到表尾即可
    • 向监视器传播命令

    call函数中有对于监视器命令传播,将命令打包为协议,发送给监视器

    详情参考:https://www.jianshu.com/p/36420adb344b

    本地缓存 - 通知机制怎么实现的?

    Redis键通知机制

    自从redis2.8以后出了一个新特性,Keyspace Notifications 称为“键空间通知”。

    这个特性大概是,凡是实现了Redis的Pub/Sub的客户端,只需要订阅相应Channel,就可以获得对Key操作的一些事件,从而可以处理一些业务。

    比如:

    1. 当你del一个key时,就可以触发一个del事件通知。
    2. 一个key的失效时间到了,就会触发expire事件通知。
    3. 对一个库所有key操作,都可以获取通知。

    注意事项:

    1)因为 Redis 目前的订阅与发布功能采取的是发送即忘(fire and forget)策略, 所以是不可靠的,并不能确保消息送达。

    2)Redis Pub/Sub 是一种并不可靠地消息机制,他不会做信息的存储,只是在线转发,那么肯定也没有ack确认机制,另外只有订阅段监听时才会转发!所以Keyspace Notification 也不是可靠地通知系统

    本地缓存的应用

    键通知机制通过发布订阅实现,具体步骤如下:
    1.修改配置:键空间通知功能耗费CPU,默认关闭,需要修改配置文件redis.conf或 操作CONFIG SET命令,设置notify-keyspace-events选项,来启用或关闭该功能(耗费cpu)。
    2.对Redis实例进行发布订阅,指定监听类和监听事件类型(键删除,过期);
    3.监听类继承JedisPubSub,实现相应操作(清除本地缓存,保证一致性);
    4.客户端进行操作,以触发订阅事件发生。

    image.png

    参数:


    image.png

    代码实现:

    import java.util.List;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    import redis.clients.jedis.JedisPubSub;
    
    public class Subscriber {
    
        public static void main(String[] args) {
            JedisPool pool = new JedisPool(new JedisPoolConfig(), "127.0.0.1", 3069);
    
            Jedis jedis = pool.getResource();
            config(jedis);
    
            jedis.psubscribe(new KeyExpiredListener(), "__key*__:*");
        }
        private static void config(Jedis jedis){
            String parameter = "notify-keyspace-events";
            List<String> notify = jedis.configGet(parameter);
    
            if(notify.get(1).equals("")){
                jedis.configSet(parameter, "KA");//参数意义见上图。
            }
        }
    }
    class KeyExpiredListener extends JedisPubSub {
    
    
        @Override
        public void onPSubscribe(String pattern, int subscribedChannels) {
            System.out.println("onPSubscribe " + pattern + " " + subscribedChannels);
        }
      // 取得订阅的消息后的处理  (key过期可以删除本地缓存)
        @Override
        public void onPMessage(String pattern, String channel, String message) {
           // 删除本地缓存
        }
    }
    

    附:redis文件事件原理:

    Redis中的文件事件关注网络IO,用于处理 Redis 服务器和客户端之间的网络IO。

    Redis基于Reactor模式开发了自己的网络事件处理器,也就是文件事件处理器。文件事件处理器使用IO多路复用技术,同时监听多个套接字,并为套接字关联不同的事件处理函数。当套接字的可读或者可写事件触发时,就会调用相应的事件处理函数。
    如下图所示,文件事件处理器有四个组成部分,它们分别是套接字、I/O多路复用程序、文件事件分派器以及事件处理器,如下图所示:


    image.png

    文件事件每当一个套接字准备好执行 accept、read、write和 close 等操作时,就会产生一个文件事件。因为 Redis 通常会连接多个套接字,所以多个文件事件有可能并发的出现。

    I/O多路复用程序负责监听多个套接字,并向文件事件派发器传递那些产生了事件的套接字。

    尽管多个文件事件可能会并发地出现,但I/O多路复用程序总是会将所有产生的套接字都放到同一个队列(也就是后文中描述的aeEventLoop的就绪事件表)里边,然后文件事件处理器会以有序、同步、单个套接字的方式处理该队列中的套接字,也就是处理就绪的文件事件。


    image.png

    所以,一次 Redis 客户端与服务器进行连接并且发送命令的过程如上图所示。

    1.客户端向服务端发起建立 socket 连接的请求,那么监听套接字将产生 AE_READABLE 事件,触发连接应答处理器执行。处理器会对客户端的连接请求进行应答,然后创建客户端套接字,以及客户端状态,并将客户端套接字的 AE_READABLE 事件与命令请求处理器关联。
    2.客户端建立连接后,向服务器发送命令,那么客户端套接字将产生 AE_READABLE 事件,触发命令请求处理器执行,处理器读取客户端命令,然后传递给相关程序去执行。
    3.执行命令获得相应的命令回复,为了将命令回复传递给客户端,服务器将客户端套接字的 AE_WRITEABLE 事件与命令回复处理器关联。当客户端试图读取命令回复时,客户端套接字产生 AE_WRITEABLE 事件,触发命令回复处理器将命令回复全部写入到套接字中。
    具体源码参考:https://www.cnblogs.com/jabnih/p/4746138.html

    SDK端统计分析热Key

    image.png

    说明:
    1.业务方应用发起执行命令;
    2.sdk首先判断是否是热key,是的话从热key模块中获取信息,否则执行正常redis命令执行流程,并异步上报命令信息到热key分析系统,对于expire,del命令也会上报到分析系统,分析系统会通知sdk中的热key模块,执行对应的删除操作,到达数据的一致性;
    3.热key分析系统:

    • 不断收集来自各个应用的sdk端的命令信息
    • 自身维护一个热key列表,每隔5秒分析一次当前命令产生的热key,并下发给对应的应用程序。
    • 当分析系统收到key失效的命令时,立即下发失效通知到sdk端热key模块,保持数据的一致性。
      以上就是整个sdk端发现处理热key的大概思路。

    Encache和guava cache的区别

    guava cache

    GuavaCache 提供了一般我们使用缓存所需要的几乎所有的功能,主要有:

    • 自动将entry节点加载进缓存结构中;
    • 当缓存的数据已经超过预先设置的最大值时,使用LRU算法移除一些数据;
    • 具备根据entry节点上次被访问或者写入的时间来计算过期机制
      (无读写回收expireAfterAccess,无更新回收expireAfterWrite,上次操作时间后再刷新refreshAfterAccess);
    • 缓存的key被封装在WeakReference引用内;
    • 缓存的value被封装在WeakReference或者SoftReference引用内;
    • 移除entry节点,可以触发监听器通知事件(同步,异步);
    • 统计:统计缓存使用过程中命中率/异常率/未命中率等数据。

    Guava Cache其核心数据结构大体上和ConcurrentHashMap一致,具体细节上会有些区别。功能上,ConcurrentMap会一直保存所有添加的元素,直到显式地移除.相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素.在某些场景下,尽管它不回收元素,也是很有用的,因为它会自动加载缓存.
    Guava Cache 官方推荐的使用场景:
    1.愿意消耗一些内存空间来提升速度;
    2.能够预计某些key会被查询一次以上;
    3.缓存中存放的数据总量不会超出内存容量(Guava Cache是单个应用运行时的本地缓存)。

    Ehcache

    特点:

    • 提供内存和磁盘存储,缓存在内存和磁盘存储可以伸缩到数G;
    • 过期策略(LRU、LFU和FIFO缓存淘汰算),也可以自定义,每个Cache的存活时间都是可以设置和控制的
    • 动态、运行时缓存配置,存活时间、空闲时间、内存和磁盘存放缓存的最大数目都是可以在运行时修改的。
    • 持久化,在VM重启后,持久化到磁盘的存储可以复原数据。
    • 缓存管理器监听器,可以监听Removed/Put/Updated/Expired 事件
    • JMX功能,可以监控和管理如下的MBean:CacheManager、Cache、CacheConfiguration、CacheStatistics
    • 分布式缓存,通过RMI、JGroups或JMS进行的异步或同步的缓存复制
    • 多种配置模式,配置文件、声明式配置、编程式配置,甚至通过指定构造器的参数来完成配置

    [图片上传中...(image.png-451bd3-1575192572195-0)]

    相关文章

      网友评论

          本文标题:2019-11-29

          本文链接:https://www.haomeiwen.com/subject/efpwwctx.html