zoukankan      html  css  js  c++  java
  • 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,看内存是否变化

  • 相关阅读:
    e621. Activating a Keystroke When Any Child Component Has Focus
    e587. Filling Basic Shapes
    e591. Drawing Simple Text
    e595. Drawing an Image
    e586. Drawing Simple Shapes
    e636. Listening to All Key Events Before Delivery to Focused Component
    在 PL/SQL 块的哪部分可以对初始变量赋予新值? (选择1项)
    Oracle数据库中,在SQL语句中连接字符串的方法是哪个?(选择1项)
    你判断下面语句,有什么作用?(单选)
    Oracle数据库表空间与数据文件的关系描述正确的是( )
  • 原文地址:https://www.cnblogs.com/mosson/p/6217134.html
Copyright © 2011-2022 走看看