国外优秀网站建设,郑州网络推广哪个好,cms开源,网站设计文档模板Redisobject 对象
对象分为键对象和值对象
键对象一般是string类型
值对象可以是string#xff0c;list#xff0c;set,zset,hash
q#xff1a;redisobj的结构
typedef struct redisObject { //类型 unsigned type:4; //编码 unsigned encoding:4; //指向底层实现…Redisobject 对象
对象分为键对象和值对象
键对象一般是string类型
值对象可以是stringlistset,zset,hash
qredisobj的结构
typedef struct redisObject { //类型 unsigned type:4; //编码 unsigned encoding:4; //指向底层实现数据结构的指针 void *ptr; //引用计数垃圾回收的时候使用int refcount;//最近被使用的时间内存淘汰的时候用unsigned lru;
} robj;q:数据类型编码和数据结构之间的对应 关系 Redis对象和数据结构的关系 键总是一个字符串对象 而值可以是五种中的一种 type 命令 得到的结果就是值的类型 可以用object encoding命令查看编码 list数据类型的编码由linkedlist和ziplist编码合并成了quicklist编码 q: 通用数据类型命令
keys * //查看当前库所有key (匹配keys *1)exists key //判断某个key是否存在,如果键存在则返回1不存在则返回0type key //查看你的key是什么类型del key //删除指定的key数据,del是一个通用命令无论值是什么数据结构类型del命令都可以将其 删除返回结果为成功删除键的个数假设删除一个不存在的键就会返回 0unlink key //根据value选择非阻塞删除,仅将keys从keyspace元数据中删除真正的删除会在后续异步操作。expire key 10 //10秒钟为给定的key设置过期时间ttl key //查看还有多少秒过期-1表示永不过期-2表示已过期select number //命令切换数据库dbsize //查看当前数据库的key的数量flushdb //清空当前库flushall //通杀全部库命令在执行前都会判断 参数是否是自己可以接收否则会返回错误 数据类型
string 字符串
q: 特点
其value是字符串不过根据字符串的格式不同又可以分为3类 string普通字符串 int整数类型可以做自增、自减操作 float浮点类型可以做自增、自减操作
q: 适用场景
String 的常见应用场景如下
常规数据比如 session、token、序列化后的对象、图片的路径的缓存计数比如用户单位时间的请求数简单限流可以用到、页面单位时间的访问数分布式锁(利用 SETNX key value 命令可以实现一个最简易的分布式锁)
q:常用命令 set key value //添加键值对*NX当数据库中key不存在时可以将key-value添加数据库*XX当数据库中key存在时可以将key-value添加数据库与NX参数互斥*EXkey的超时秒数*PXkey的超时毫秒数与EX互斥get key //查询对应键值append key value //将给定的value 追加到原值的末尾,返回长度strlen key //获得值的长度setnx key value //只有在 key 不存在时 设置 key 的值incr key //将 key 中储存的数字值增1 只能对数字值操作如果为空新增值为1decr key //将 key 中储存的数字值减1 只能对数字值操作如果为空新增值为-1incrby / decrby key 步长 //将 key 中储存的数字值增减。自定义步长。mset key1value1key2value2 ..... //同时设置一个或多个 key-value对 mget key1key2key3 ..... //同时获取一个或多个 value msetnx key1value1key2value2 ..... //同时设置一个或多个 key-value 对当且仅当所有给定 key 都不存在。 原子性有一个失败则都失败getrange key起始位置结束位置 //得值的范围类似java中的substring前包后包setrange key起始位置value //用 value 覆写key所储存的字符串值从起始位置开始(索引从0开始)。setex key过期时间value //设置键值的同时设置过期时间单位秒。getset keyvalue //以新换旧设置了新值同时获得旧值。q底层编码方式和数据结构 当存储的是long 数字的时候使用int编码prt直接存储数字 不包括浮点数 当存储的字符串小于44个字节的时候使用embstr编码字符串和redisobject存储在一起当存储的字符串大于44个字节的时候使用raw编码prt存储的是sds的地址指针
set 集合
q: 特点 无序 元素不可重复 查找快 支持交集、并集、差集等功能
q: 适用场景
应用场景
需要存放的数据不能重复的场景 不可重复下单点赞 需要获取多个数据源交集、并集和差集的场景 共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐差集、音乐推荐差集、订阅号推荐差集交集 等场景。 需要随机获取数据源中的元素的场景 抽奖系统、随机点名等场景。相关命令SPOP随机获取集合中的元素并移除适合不允许重复中奖的场景、SRANDMEMBER随机获取集合中的元素适合允许重复中奖的场景。
q:常用命令
sadd keyvalue1value2 ..... //将一个或多个 member 元素加入到集合 key 中已经存在的 member 元素将被忽略smembers key //取出该集合的所有值。sismember keyvalue //判断集合key是否为含有该value值有1没有0scardkey //返回该集合的元素个数。srem keyvalue1value2 .... //删除集合中的某个元素。spop key //随机从该集合中吐出一个值。srandmember keyn //随机从该集合中取出n个值。不会从集合中删除 。smove sourcedestinationvalue //把集合中一个值从一个集合移动到另一个集合sinter key1key2 //返回两个集合的交集元素。sunion key1key2 //返回两个集合的并集元素。sdiff key1key2 //返回两个集合的差集元素(key1中的不包含key2中的)q底层编码方式和数据结构
当存储的所有数据都是整数元素数量不超过set-max-intset-entries时Set会采用IntSet编码以节省内存,底层数据结构是intset当存储的所有数据不都是整数或元素数量超过set-max-intset-entries时set采用hashtable编码底层是Dict中的key用来存储元素value统一为null。
hash 哈希
q: 特点
hash也叫散列 是一个键值对集合。
q: 适用场景
hash特别适合用于存储对象。 q:常用命令
hset keyfieldvalue field2value2 //给key集合中的 field键赋值valuehget key1field //从key1集合field取出 valuehmset key1field1value1field2value2... //批量设置hash的值,hmset被弃用可以用hset做到hexistskey1field //查看哈希表 key 中给定域 field 是否存在。hkeys key //列出该hash集合的所有fieldhvals key //列出该hash集合的所有valuehincrby keyfieldincrement //为哈希表 key 中的域 field 的值加上增量 1 -1hsetnx keyfieldvalue //将哈希表 key 中的域 field 的值设置为 value 当且仅当域 field 不存在 .q底层编码方式和数据结构 Hash结构默认采用ZipList编码用以节省内存。 ZipList中相邻的两个entry 分别保存field和value底层数据结构式ziplist 当数据量较大时Hash结构会转为hashtable编码底层数据结构是Dict触发条件有两个 ZipList中的元素数量超过了hash-max-ziplist-entries默认512ZipList中的任意entry大小超过了hash-max-ziplist-value默认64字节 节点过多或单个节点过大 zset/sorted set 有序集合
q: 特点
无重复有序 每个成员都关联了一个评分score,这个评分score被用来按照从最低分到最高分的方式排序集合中的成员。 集合的成员是唯一的但是评分可以是重复了 。 q: 适用场景
适合范围或者排序的应用场景
排行榜
q:常用命令
zadd keyscore1value1score2value2… //将一个或多个 member 元素及其 score 值加入到有序集 key 当中。zrange keystartstop [WITHSCORES] //返回有序集 key 中下标在startstop之间的元素,带WITHSCORES可以让分数一起和值返回到结果集。zrangebyscore key minmax [withscores] [limit offset count] //返回有序集 key 中所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。 zrevrangebyscore key maxmin [withscores] [limit offset count] //同上改为从大到小排列。 zincrby keyincrementvalue // 为元素的score加上增量zrem keyvalue //删除该集合下指定值的元素 zcount keyminmax //统计该集合分数区间内的元素个数 zrank keyvalue //返回该值在集合中的排名从0开始。q底层编码方式和数据结构
当满足下面条件时采用ziplist编码底层数据结构是ziplist 元素数量小于zset_max_ziplist_entries默认值128每个元素都小于zset_max_ziplist_value字节默认值64 ziplist本身没有排序功能而且没有键值对的概念因此需要有zset通过编码实现 - ZipList是连续内存因此score和element是紧挨在一起的两个entry element在前score在后 - score越小越接近队首score越大越接近队尾按照score值升序排列 否则采用zset编码底层是zset数据结构zset的数据结构又指向skiplist和dict SkipList可以排序并且可以同时存储score和ele值memberDict可以键值存储并且可以根据key找value,实现O(1)的查找 二者实际上共用对象不会造成内存的浪费 list 列表
q: 特点
双向链表结构。既可以支持正向检索和也可以支持反向检索。
特征也与LinkedList类似
单键多值有序元素可以重复插入和删除快查询速度一般
q: 适用场景
应用场景:
常用来存储一个有序数据例如朋友圈点赞列表评论列表等。可以用来做消息队列只是功能过于简单且存在很多缺陷不建议这样做。
q:常用命令
lpush/rpush keyvalue1value2value3 //.... 从左边/右边插入一个或多个值。lpush是头插法lpop/rpop key //从左边/右边吐出一个值。值在键在值光键亡。rpoplpush key1key2从key1 //列表右边吐出一个值插到key2列表左边。lrange keystartstop //按照索引下标获得元素(从左到右)lrange key 0 -1 //0左边第一个-1右边第一个0 -1表示获取所有lindex keyindex //按照索引下标获得元素(从左到右)llen key //获得列表长度 linsert key before/after valuenewvalue //在value的前面、后面插入newvalue插入值lrem keynvalue //从左边删除n个value(从左到右)lsetkeyindexvalue //将列表key下标为index的值替换成valueq底层编码方式和数据结构
List的编码方式是quicklist,底层数据结构为快速链表quickListquicklist的节点又指向了ziplist Redis 3.2 之前List 底层实现是 LinkedList 或者 ZipList。 Redis 3.2 之后引入了 LinkedList 和 ZipList 的结合 QuickListList 的底层实现变为 QuickList。 首先在列表元素较少的情况下会使用一块连续的内存存储这个结构是ziplist也即是压缩列表。(它将所有的元素紧挨着一起存储分配的是一块连续的内存。)
当数据量比较多的时候才会改成quicklist。
因为普通的链表需要的附加指针空间太大会比较浪费空间。比如这个列表里存的只是int类型的数据结构上还需要两个额外的指针prev和next。 Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能又不会出现太大的空间冗余。
数据结构
sds 简单动态字符串
qsds的结构
sds的结构
struct sdshdr {
//记录buf数组中已使用字节的数量
//等于SDS所保存字符串的长度
int len;
//记录buf数组中未使用字节的数量
int free;
//字节数组用于保存字符串
char buf[];
}; q: sds和c字符串的区别 相同点
都是用char数组来记录字符最后都有一个\0来代表字符串结束 sds最后也用\0代表结束是为了重用c语言字符串的一些函数例如printf打印而不用重写所有的函数 不同点 肯定是c语言字符串存在一定的缺陷redis才会重写那么这些既是redis和c语言字符串的不同点也是redis sds的优点 常数级别获取字符串长度sds获取字符串长度的时间复杂度是O(1),c语言是O(n),通过len的冗余存储来实现杜绝缓冲区溢出字符串在拼接之前可以做内存检查确保空间充足否则进行扩充不会像c语言一样造成内存溢出减少修改字符串时带来的内存重分配次数预分配空间free来减少内存重分配的次数可以提升性能二进制安全c语言字符串通过\0空字符来标志字符串结束因此不能包含空字符而sds通过len来表示字符串结束可以包含空字符可以存储图片等二进制信息因此是二进制安全的
q: 容量扩充机制
如图中所示内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。
当字符串长度小于1M时扩容都是加倍现有的空间如果超过1M扩容时一次只会多扩1M的空间。需要注意的是字符串最大长度为512M。
intset 正数集合
q: intset的结构
typedef struct intset { //编码方式 uint32_t encoding; //集合包含的元素数量 uint32_t length; //保存元素的数组 int8_t contents[];
} intset;数组升序排列没有重复
q: 如何升级
当新元素很大的时候集合要升级成更大的编码方式 升级整数集合并添加新元素共分为三步进行 1根据新元素的类型扩展整数集合底层数组的空间大小并为新元素分配空间。 2将底层数组现有的所有元素都转换成与新元素相同的类型并将类型转换后的元素放置到正确的位上而且在放置元素的过程中需要继续维持底层数组的有序性质不变。 3将新元素添加到底层数组里面。 ❑升级操作为整数集合带来了操作上的灵活性并且尽可能地节约了内存。 ❑整数集合只支持升级操作不支持降级操作。
dictht 哈希表
typedef struct dictht { //哈希表数组 dictEntry **table; //哈希表大小 unsigned long size; //哈希表大小掩码用于计算索引值 //总是等于size-1 unsigned long sizemask; //该哈希表已有节点的数量 unsigned long used;
} dictht;
哈希表结点
typedef struct dictEntry { //键 void *key; //值 union{ void *val; uint64_tu64; int64_ts64; } v; //指向下个哈希表节点形成链表 struct dictEntry *next;
} dictEntry;q如何解决哈希冲突
用拉链法的头插法解决哈希冲突
dict 字典
typedef struct dict { //类型特定函数 dictType *type; //私有数据 void *privdata; //哈希表 dictht ht[2]; // rehash索引 //当rehash不在进行时值为-1 in trehashidx; /* rehashing not in progress if rehashidx -1 */
} dict;ht有两个一般只使用第一个第二个哈希表只在rehash的时候用
插入数据的时候先计算哈希值再计算索引值再插入到指定位置
q : 如何rehash
随着操作的不断执行哈希表保存的键值对会逐渐地增多或者减少为了让哈希表的负载因子load factor维持在一个合理的范围之内当哈希表保存的键值对数量太多或者太少时程序需要对哈希表的大小进行相应的扩展或者收缩。
扩展和收缩哈希表的工作可以通过执行rehash重新散列操作来完成Redis对字典的哈希表执行rehash的步骤如下
1为字典的ht[1]哈希表分配空间这个哈希表的空间大小取决于要执行的操作以及ht[0]当前包含的键值对数量也即是ht[0].used属性的值 ❑如果执行的是扩展操作那么ht[1]的大小为第一个大于等于ht[0].used*2的2 n2的n次方幂 ❑如果执行的是收缩操作那么ht[1]的大小为第一个大于等于ht[0].used的2 n。
2将保存在ht[0]中的所有键值对rehash到ht[1]上面rehash指的是重新计算键的哈希值和索引值然后将键值对放置到ht[1]哈希表的指定位置上。
3当ht[0]包含的所有键值对都迁移到了ht[1]之后ht[0]变为空表释放ht[0]将ht[1]设置为ht[0]并在ht[1]新创建一个空白哈希表为下一次rehash做准备。
为ht[1]分配空间复制ht[0]的数据到ht[1],释放ht[0],把ht[1]设为ht[0],ht[1]创建一个空哈希表
q:什么时候rehash
哈希表的扩展与收缩 哈希表的扩展与收缩当以下条件中的任意一个被满足时程序会自动开始对哈希表执行扩展操作 1服务器目前没有在执行BGSAVE命令或者BGREWRITEAOF命令并且哈希表的负载因子大于等于1。 2服务器目前正在执行BGSAVE命令或者BGREWRITEAOF命令并且哈希表的负载因子大于等于5。
为什么rehash是渐进式
如果ht[0]的数据非常多那么把数据全部转移到ht[1]将会非常耗费时间因此这个过程是分多次渐进式完成的 rehashidx记录了正在转移的索引下标当转移完成会置为-1 因为在进行渐进式rehash的过程中字典会同时使用ht[0]和ht[1]两个哈希表所以在渐进式rehash进行期间字典的删除delete、查找find、更新update等操作会在两个哈希表上进行。例如要在字典里面查找一个键的话程序会先在ht[0]里面进行查找如果没找到的话就会继续到ht[1]里面进行查找诸如此类。
另外在渐进式rehash执行期间新添加到字典的键值对一律会被保存到ht[1]里面而ht[0]则不再进行任何添加操作这一措施保证了ht[0]包含的键值对数量会只减不增并随着rehash操作的执行而最终变成空表。
ziplist 压缩列表
q: ziplist的结构 每个压缩列表节点可以保存一个字节数组或者一个整数值 q: ziplist过大的时候有什么缺点
当ziplist变得很⼤的时候它有如下几个缺点
每次插⼊或修改引发的realloc操作会有更⼤的概率造成内存拷贝从而降低性能。⼀旦发生内存拷贝内存拷贝的成本也相应增加因为要拷贝更⼤的⼀块数据。当ziplist数据项过多的时候在它上⾯查找指定的数据项就会性能变得很低因为ziplist上的查找需要进行遍历。
skiplist 跳表
skiplist是多层级不同跨度的链表
q: skiplist的结构 zsikpList
typedef struct zskiplist { //表头节点和表尾节点 structz skiplistNode *header, *tail; //表中节点的数量 unsigned long length; //表中层数最大的节点的层数 int level;
} zskiplist;
zskipListNode
typedef struct zskiplistNode { //层 struct zskiplistLevel { //前进指针 struct zskiplistNode *forward; //跨度 unsigned int span; } level[]; //后退指针 struct zskiplistNode *backward; //分值 double score; //成员对象 robj *obj;
} zskiplistNode;每个结点的成员对象是唯一的但是分值可以相同分值相同就按照成员对象由小到大排序整个链表都是按照分值由小到大排序 q: skiplist如何遍历
遍历 首先遍历高层级跨度大的指针如果过大就遍历下一层级
zset
q:zset的结构
typedef struct zset { zskiplist *zsl; dict *dict;
} zset;SkipList可以排序并且可以同时存储score和ele值memberDict可以键值存储并且可以根据key找value,实现O(1)的查找 二者实际上共用对象不会造成内存的浪费 quicklist 快表
q:quicklist的结构
quicklist 实际上是 zipList 和 linkedList 的混合体它将 linkedList 按段切分每一段使用 zipList 来紧凑存储多个 zipList 之间使用双向指针串接起来。 typedef struct quicklist { quicklistNode *head; quicklistNode *tail; unsigned long count; /* total count of all entries in all ziplists */ unsigned long len;
} quicklist;typedef struct quicklistNode { struct quicklistNode *prev; //上一个node节点 struct quicklistNode *next; //下一个node unsigned char *zl; //保存的数据 压缩前ziplist 压缩后压缩的数据 unsigned int sz; /* ziplist size in bytes */ unsigned int count : 16; /* count of items in ziplist */ } quicklistNode;