• Redis


    Redis:

    原理:数据结构、过期机制、淘汰机制

    实践:内存分析、最佳实践


    数据结构:

    最基本的数据结构(最基本、最简洁)

    RedisObject:
    
    typedef struct redisObject{
        unsigned type:4;        #  type  4bit
        unsigned encoding:4;  # encoding  4bit 
        unsigned lru:24;     #类似于  最近访问时间这种  用于做key淘汰
        int refcount;       #引用计数 redis里面的数据可以通过引用计数进行共享
        void *ptr;        #  数据结构可以存任意类型的数据
    }robj;
    

    redis 里面有不超过16种的类型;有不超过16种的编码方式,一种类型可能有多种编码方式,共享的数据是可以共享的

    数据结构的第一个成员:type

    redis 实际上有五种数据类型:  List、 Set、 String、 Sorted Set、Hash

    编码方式:encoding

    数据类型与编码方式的对应关系:

    数据类型都是O1的

    String - RAW(裸的--纯字符串的结构)

    RedisObject【type: String/Encoding:RAW/LUR/RefCount/ptr】ptr-->指向一个叫sds的数据结构:sds是redis里面保存一般字符串的数据结构,sds包含了(头):Len:45/Free:3

    redis里面字符串比较小(仅限 <= 39 )的时候的优化

    String - EMBSTR【type: String/Encoding:EMBSTR/ptr sds:len/sds:free/sds:buf】

    obj和字符串在同一个连续的内存块上

    String - INT【type:String/Encoding:INT...ptr(INT值)】ptr的值直接代表对应的Value

    共享条件(0-10000,LUR无意义)

     String编码方式的总结:

      *整数类型的value比普通的value节省内存

      *0-10000,LUR无效的情况下String Object无需额外创建,既省内存又省时间

       *长度<=39的使用EMBSTR编码,效率更高

    ZIPLIST/INTSET:压缩列表(数据量小的时候用)

    *紧凑连续的一段内存;5-10倍的压缩比

    RedisObject【type:List/Set/Hash/Sorted Set,Encoding:ZIPLIST/INTSET...ptr】

      ptr-->ziplist[zlbytes/zltail/entrey1/entry2...entryn/zlend]

    HT SIZE&REHASH

    *HT默认初始大小为4

    *负载因子超出合理范围(0.1-5)时进行扩缩容(rehash)

    *HT[0]、HT[1]

    *一次性rehash太多的key可能导致服务长时间不可用,resdis采用渐进式rehash分批进行

    DICT

    *DICT对HT进行封装

    *读写均为O(1)

    *Redis中广泛存在

      哈希对象,集合对象和有序集合对象

      db[n]->dict,db[n]->expire

    DICT渐进式rehash

    *每次对字典执行添加、删除、查找或者更新操作时,除了执行指定的操作外,还会顺带将HT[0],哈希表在rehashindex索引上的所有键值对rehash到HT[1]上,并将rehashindex的值增1;

    *直到整个HT[0]全部完成rehash后,rehashindex设置为-1,释放HT[0],HT[1]置为HT[0],在HT[1]创建一个新的空白表

    SKIPLIST :跳跃表(有序集合)

    *跳跃表:随机化的平衡树

    *while ((random) < (0.25 * 0xFFFF)) level += 1

    *第n+1层的节点数目为第n层的1/4

    *性能与平衡树近似O(lgn)

    性能:

    Redis的过期机制:

    *失效性数据,比如限时优惠活动,缓存或者验证码可以采用Redis过期机制进行管理

    *expire|pexpire key ttl

    *typedef struct redisDb{

      dict *dict;//所有的 k v

      dict *expire;//设置过期时间的kv

      }redisDb;

    *db->expire会复用db->dict中的key,value对象

    *访问key时:expireIFNeeded(db,key);

    *一次事件循环结束,进入事件侦听前:

      activeExpireCycle(ACTIVE_EXPIRE_CYCLE_FAST)

      ACTIVE_EXPIRE_CYCLE_SLOW

    *系统空闲时后台定期任务

      activeExpireCycle(ACTIVE_EXPIRE_CYCLE_SLOW)

      25%CPU时间限制

    后台定期任务 serverCron

    *执行时间间隔1/hz 默认100ms(1s执行10次)

      过期key清理

      rehash全局dict

      关闭超时客户端

      主从同步相关操作

    过期key清理算法

    *依次遍历所有的db

    *从db中随机取20个key,判断是否过期,若过期则逐出

    *若5个以上key过期,则重复执行遍历,否则遍历下一个db

    *在清理过程中,若达到了时间限制退出清理过程

    过期key清理算法的特点

    *这是一个基于概率的简单算法,假设是抽出的样本能够代表整个key空间

    *单次运行时间有25% cpu时间限制

    *redis持续清理过期的数据直置将过期的key的百分比降到25%以下

    *长期来看任何给定的时刻已经过期的数据仍占着内存空间的key的量最多为每秒的写操作量除以4

    *永远都不可能清理干净

    *清理过程以key为单位,如果有大key存在,删除耗时太长有可能导致长时间服务不可用

    *调高HZ参数

      可以提升淘汰过期key的频率

      相应的,每一次淘汰最大时间限制将减少(可使系统响应时间变快)

      在一般情况下并不能降低过期key所占比率

      会导致空闲时cpu的占用率提高

    淘汰机制:

    *执行命令式,先判断是否设置了最大允许内存(server.maxmermy)

    *若是,调用freeMemoryIfNeeded,先判断使用内存是否超出最大内存机制

    *若是,按照设置的淘汰策略淘汰key直到使用内存小于最大内存

    *volatile-lru:从已设置过期时间的数据中挑选最近最少使用的数据淘汰

    *volatile-ttl:从已设置过期时间的数据中挑选将要过期的数据淘汰

    *volatile-random:从已设置过期时间的数据集中任意选择数据淘汰

    *allkeys-lru: 从数据集中挑选最近最少使用的数据淘汰

    *allkeys-random:从数据集中任意选择数据淘汰

    *no-enviction:禁止淘汰数据

    *大Key

      删除大key耗时长

      写入大key导致内存超出太多,下次淘汰需要淘汰很多内存

    *内存长期100%问题

      每一次执行命令都需要淘汰一些key

    *内存100%且无key可淘汰的情况

      OOM command not allowed when used memory > 'maxmemory'

    内存分析:提升业务特点、了解业务瓶颈、发现业务bug

    离线内存分析:生成rdb文件(bgsave)、生成内存快照、分析内存快照

    redis(rdb文件) 分析工具:redis-rdb-tools

    使用PYPI安装 :pip install rdbtools

    源码安装:git clone https://github.com/sripathikrishnan/redis-rdb-tools

      cd redis-rdb-tools

      sudo python setup.py install

      rdb -c memory dump.rdb > memory.csv

    *生成内存快照为csv格式,包含

      Database:数据库ID

      Type:数据类型

      key:

      size_in_bytes:理论内存值

      Encoding:编码方式

      num_elements:成员个数

      len_largest_element:最大成员长度

    数据分析: 

    数据导入数据库:

    *sqlite3 memory.db

    *sqlite > create table

      memory(database int,type varchar(128),key varchar(128),

      size_in_bytes int,encoding varchar(128),num_elsments int,

      len_largest_element varchar(128));

    *sqlite > .mode csv memory

    *sqlite > .import memory.csv memory

    查询key的个数

      sqlite > select count(*) from memory;

    查询总的内存占用

      sqlite > select sum(size_in_bytes) from memory;

    查询内存占用最高的十个key

      sqlite > select * from memory order by size_in_bytes desc limit 10;

    查询成员个数1000以上的list

      sqlite > select * from memory where type='list' and num_elsments > 1000;

    在线内存分析:

    *查看client占用内存

      对比master,slave内存情况

      client list 查看 idle,multi

    *排除大的dict  rehash占用

      静态分析出大key 若有dict类型的数据

      脚本访问触发rehash,看内存是否变化

  • 相关阅读:
    记录时间开销的好处
    jQuery实现轮播图效果
    读《论证是一门学问》
    sqrt.java
    关于perl:Fatal: failed to find source perl5db.pl for epic_breakpoints.pm解决方法
    JAVA基础——对象与引用概念(转载)
    对百度搜索看法的转变
    C输出格式——转载
    Java static关键字与静态块
    简单js实现弹出登陆框div层,背景变暗不可操作
  • 原文地址:https://www.cnblogs.com/mosson/p/6217134.html
Copyright © 2020-2023  润新知