zoukankan      html  css  js  c++  java
  • Redis学习

    Reids

    Reference

    [1] https://zhuanlan.zhihu.com/p/62608469

    解决问题
    主要为了解决性能问题. 
    • 计数器&统计数据: 排行榜
      • 列出前100名用户 zset
      • 列出某用户当前名词
    • 任务队列: 定时任务(把时间戳和用户一起做为key放到list里被消费),推送
      • 针对不同类型的用户有不同的任务组合 (把任务放到list列表里,消费者不断消费列表)
    • 关注
      • 粉丝列表
      • 关注列表
    • 限流器

    Redis技术

    事件循环

    redis是单线程的,整个redis服务在启动之后会陷入一个巨大的while循环,不停地处理文件事件(先处理)和时间事件(后处理).
    文件事件: 新建链接等响应请求事件.epoll方式,在多个客户端中实现多路复用,接受它们发来的命令请求,并将命令的执行结果返回给客户端.
    时间事件: 记录那些要在指定时间点运行的事件,多个时间事件以无序链表的形式保存在服务器状态中.
    内部定时的任务如: 
    • 更新服务器各类统计信息,如事件、内存占用等
    • 数据库后台操作,key过期清理、数据库rehash等
    • 关闭、清理失效的客户端连接
    • 检查是否需要RDB dump(全部数据拷贝的磁盘,会新建进程去做),AOF重写
    • 主节点,对从节点定期同步
    • 集群模式,集群定期同步信息和连接测试
    beforesleep阶段 =》 进入event loop前执行
    • cluster集群状态检查,ok-》fail, fail-》ok
    • 处理被block住的client,如一些阻塞请求BLPOP等
    • 将AOF buffer持久化到AOF文件
    redis大体流程: beforeSleep-》epollwait-》处理请求-》定时事件-》...
    建议: 减少大key(> 1M),减少耗时命令...

    过期&逐出

    逐出
    当执行write但内存达到上限时,强制将一些key删除.删除key的策略:
    • allkeys - 所有key
    • volatile - 设置了过期的key
    • LRU - 最久未被使用
    • random - 随机
    • ttl - 最快过期的
    特点
    • 不是精准算法,而是抽样比对
    • 每次写入操作前判断
    • 逐出会block当前请求
    建议: 关注逐出qps,过高会影响正常请求处理.redis内部会用hash来记录一些内部关系,达到一定容量限制后会翻倍的rehash,这个阶段持续的几秒钟有可能造成写入key时大量逐出.
    过期
    当某个key到达了ttl事件,认为该key已经失效.
    两种方式:
    • 惰性删除 - 读写操作前判断ttl,如过期则删除
    • 定期删除 - 在redis定时事件中随机抽取部分key判断ttl
    特点
    • 并不一定是按设置时间准时的过期
    • 定期删除的时候会判断过期比例,达到阈值才退出
    建议:打散key的过期时间,避免大量key在同一时间点过期

    持久化

    将内存中的数据dump到磁盘文件,两种方式
    • RDB持久化到文件中
      • 经过压缩的二进制格式
      • fork子进程dump可能造成瞬间卡顿
    • AOF持久化(推荐)
      • 保存所有修改数据库的命令
      • 先写aof缓存,再同步到aof文件(异步)
      • aof重写,达到阈值时触发,减小文件大小
    应用:利用AOF文件备灾
    • 可将数据恢复到最近3天任意小时粒度

    主从复制

    主从模式
    • 主、从节点都可以挂从节点(扛读流量)
    • 最终一致性
    全量同步
    • 传递RDB文件&restore命令重建kv
    • 传递在RDB dump过程中的写入数据
    部分同步
    • 根据offset传递积压缓存中的部分数据

    pipeline&mget

    pipeline

    • Client: 将多个命令缓存起来,缓冲区满了就发送
    • Redis: 处理一个tcp连接发来的多个命令;处理完一个就发一个
    • Twemproxy: 既要处理一个client连接发来的多个命令,又要将同一个下游redis server的命令缓存起来一起发送
    信息链路:
    Redis client -》 proxy -》redis server
    优点:
    • 节省往返时间
    • 减少proxy,redis server的IO次数

    Mget

    • Client: 使用mget命令
    • Redis: 一个命令中处理多个key;等所有key处理完后组装回复一起发送
    • Twemproxy: 拆key分发到不同redis server;需要等待、缓存mget中全部回复
    优点:
    • 节省往返时间
    缺点:
    • proxy缓存mget结果
    • mget延时是最后一个key回复时间,前面的key需要等待
    建议:利用pipeline代替mget,且控制一次请求的命令数量(建议50以内)

    redis集群

    • 一致性hash方式
    实例宕机、加节点容易造成数据丢失
    • 原生redis cluster方式:
    每个实例都有主有从,两两(master)之间进行通信,有节点数量限制

    为什么Redis单线程却能支撑高并发

    I/O多路复用, Redis 服务采用 Reactor 的方式来实现文件事件处理器(每一个网络连接其实都对应一个文件描述符). 文件事件处理器使用 I/O 多路复用模块同时监听多个 FD,当 accept、read、write 和 close 文件事件产生时,文件事件处理器就会回调 FD 绑定的事件处理器。虽然整个文件事件处理器是在单线程上运行的,但是通过 I/O 多路复用模块的引入,实现了同时对多个 FD 读写的监控,提高了网络通信模型的性能,同时也可以保证整个 Redis 服务实现的简单。
     
    Redis 会优先选择时间复杂度为 $O(1)$ 的 I/O 多路复用函数作为底层实现,包括 Solaries 10 中的 evport、Linux 中的 epoll 和 macOS/FreeBSD 中的 kqueue,上述的这些函数都使用了内核内部的结构,并且能够服务几十万的文件描述符。
    但是如果当前编译环境没有上述函数,就会选择 select 作为备选方案,由于其在使用时会扫描全部监听的描述符,所以其时间复杂度较差 $O(n)$,并且只能同时服务 1024 个文件描述符,所以一般并不会以 select 作为第一方案使用。

    Redis使用规范

    一、键值设计

    1、key名设计
    可读性和可管理性
    以业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如业务名:表名:id
    ugc:video: 1
    简洁性
    保证语义的前提下,控制key的长度,当key较多时,内存占用也不容忽视,例如:
    user:{uid}:friends:messages:{mid}简化为u:{uid}:fr:m:{mid}。
    不要包含特殊字符
    反例:包含空格、换行、单双引号以及其他转义字符
    2、value设计
    拒绝bigkey
    防止网卡流量、慢查询,string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。
    反例:一个包含200万个元素的list。
    非字符串的bigkey,不要使用del删除,使用hscan、sscan、zscan方式渐进式删除,同时要注意防止bigkey过期时间自动删除问题(例如一个200万的zset设置1小时过期,会触发del操作,造成阻塞,而且该操作不会不出现在慢查询中(latency可查)),查找方法和删除方法
    选择适合的数据类型
    例如:实体类型(要合理控制和使用数据结构内存编码优化配置,例如ziplist,但也要注意节省内存和性能之间的平衡)。了解下,Redis 为什么这么快?
    反例:
    set user: 1 :name tom set user: 1 :age 19 set user: 1 :favor football
    正例:
    hmset user: 1 name tom age 19 favor football
    控制key的生命周期
    redis不是垃圾桶,建议使用expire设置过期时间(条件允许可以打散过期时间,防止集中过期),不过期的数据重点关注idletime。

    二、命令使用


    1、O(N)命令关注N的数量
    例如hgetall、lrange、smembers、zrange、sinter等并非不能使用,但是需要明确N的值。有遍历的需求可以使用hscan、sscan、zscan代替。
    2、禁用命令
    禁止线上使用keys、flushall、flushdb等,通过redis的rename机制禁掉命令,或者使用scan的方式渐进式处理。一个致命的 Redis 命令,导致公司损失 400 万!!关注Java技术栈微信公众号,在后台回复关键字:redis,可以获取更多栈长整理的 Redis 系列技术干货。
    3、合理使用select
    redis的多数据库较弱,使用数字进行区分,很多客户端支持较差,同时多业务用多数据库实际还是单线程处理,会有干扰。
    4、使用批量操作提高效率

    1. 原生命令:例如mget、mset。
    2. 非原生命令:可以使用pipeline提高效率。

    但要注意控制一次批量操作的元素个数(例如500以内,实际也和元素字节数有关)。
    注意两者不同:

    1. 原生是原子操作,pipeline是非原子操作。
    2. pipeline可以打包不同的命令,原生做不到
    3. pipeline需要客户端和服务端同时支持。

    5、不建议过多使用Redis事务功能
    Redis的事务功能较弱(不支持回滚),而且集群版本(自研和官方)要求一次事务操作的key必须在一个slot上(可以使用hashtag功能解决)。
    6、Redis集群版本在使用Lua上有特殊要求
    1、所有key都应该由 KEYS 数组来传递,redis.call/pcall 里面调用的redis命令,key的位置,必须是KEYS array, 否则直接返回error,"-ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS arrayrn"
    2、所有key,必须在1个slot上,否则直接返回error, "-ERR eval/evalsha command keys must in same slotrn"
    7、monitor命令
    必要情况下使用monitor命令时,要注意不要长时间使用。

    三、客户端使用

    1、避免多个应用使用一个Redis实例
    不相干的业务拆分,公共数据做服务化。
    2、使用连接池
    可以有效控制连接,同时提高效率,标准使用方式:
    Jedis jedis = null; try { jedis = jedisPool.getResource(); //具体的命令 jedis.executeCommand() } catch (Exception e) { logger.error("op key {} error: " + e.getMessage(), key, e); } finally { //注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。 if (jedis != null) jedis.close(); }
    3、熔断功能
    高并发下建议客户端添加熔断功能(例如netflix hystrix)
    4、合理的加密
    设置合理的密码,如有必要可以使用SSL加密访问(阿里云Redis支持)
    5、淘汰策略
    根据自身业务类型,选好maxmemory-policy(最大内存淘汰策略),设置好过期时间。
    默认策略是volatile-lru,即超过最大内存后,在过期键中使用lru算法进行key的剔除,保证不过期数据不被删除,但是可能会出现OOM问题。
    其他策略如下:

    • allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性,直到腾出足够空间为止。
    • allkeys-random:随机删除所有键,直到腾出足够空间为止。
    • volatile-random:随机删除过期键,直到腾出足够空间为止。
    • volatile-ttl:根据键值对象的ttl属性,删除最近将要过期数据。如果没有,回退到noeviction策略。
    • noeviction:不会剔除任何数据,拒绝所有写入操作并返回客户端错误信息"(error) OOM command not allowed when used memory",此时Redis只响应读操作。

    四、相关工具

    1、数据同步
    redis间数据同步可以使用:redis-port
    2、big key搜索
    redis大key搜索工具
    3、热点key寻找
    内部实现使用monitor,所以建议短时间使用facebook的redis-faina
    阿里云Redis已经在内核层面解决热点key问题

    五、删除bigkey

      1. 下面操作可以使用pipeline加速。
      2. redis 4.0已经支持key的异步删除,欢迎使用。
     
  • 相关阅读:
    同一位置(同一个ImageView)显示不同的图片levellist (转)
    LinearLayout android:layout_weight的理解
    给C盘减减肥,让你电脑飞一般速度!
    (Android实战)界面设计注意事项
    利用sender的Parent获取GridView中的当前行(转载)
    用SQL查看字符串ASCII码
    自己写DataPage分页
    连接长宽高的UDF
    把Dictionary绑定到combox
    trackbar和processbar功能合并
  • 原文地址:https://www.cnblogs.com/codingforum/p/13253608.html
Copyright © 2011-2022 走看看