Redis每个键值对都是由对象组成,其中
1.数据库键总是一个字符串对象 sorted set obj
2.数据库键的值可以是 字符串对象、列表对象、哈希对象、集合对象、有序对象
包含字符串的键值对底层实现为SDS(Simple Dynamic String)
struct sds{
int len; //记录buf数组已使用的字节数量
int free; //记录buf数组未使用的字节数量
char buf[]; //字节数组,用于保存字符串
}
获取 长度为O(1) ,防止 扩容空间不足 ,会进行自动扩容
空间预分配(减少 字符串增长 内存重分配次数,避免频繁重新分配空间导致时间浪费)
1.若len<1MB,则free=len 即大小为 2×len+1字节
2.若len>1MB,则free=1MB 即大小为 len+1MB+1字节
惰性空间释放(字符串缩短 空间不释放,而是把多出来的字节放到free里,理由同上)
二进制安全
可以保存任意格式二进制数据(文本、视频、图片等),而不用担心数据丢失
如:c字符串(只能保存文本) redis\0cluster\0 只显示redis
兼容部分C字符串函数 ,遵循C字符串以空字符(\0)结尾的惯例
链表 高效的节点重排 顺序性的访问方式 灵活调整长度
使用地方:链表键、发布与订阅、慢查询、监视器、客户端状态信息、客户端输出缓存区
特性: 双端、无环(null为终点)、带头尾指针、计数器、多态(数据用无类型指针void)
dup复制节点保存的值 free释放节点值 match比较是否相等
(哈希键、数据库的底层实现)
字典(使用hash表实现,用链地址法解决冲突) 又称 符号表、关联数组、映射 用于保存键值
对的抽象数据结构
struct dictht{
dictEntry ** table; //哈希表数组
unsigned long size; //大小
unsigned long sizemask; //大小掩码,用于计算索引值总是等于size-1
unsigned long used //已有节点数量
}dictht;
struct dictEntry{
void * key; //键
union{ //值
void * val;
uint64_tu64; //整数
int64_ts64; //整数
}v;
struct dictEntry * next;
}dictEntry;
字典
struct dict{
dictType * type; //类型特定函数 为创建多态字典而设置
void privdata; //私有数据 保存需要传给特定函数的可选参数
dictht ht[2]; //哈希表 一般使用ht【0】 另外一个rehash才使用
int rehashidx; //rehash不在进行时值为-1 ,按这个作为数组下标
}
hash=dict->type->hashFunction(key);
index = hash & dict->ht[0].sizemask;
rehash时 扩展操作h[1]的大小为第一个大于等于h[0].used×2 的2^n
收缩操作h[1]的大小为第一个大于等于h[0].used 的2^n
rehash后 释放h[0],然后将h[1]设为h[0],并且在h[1]新建一个空白表
rehash条件 扩展 服务器未执行执行bgsave或bgrewriteasf命令 负载因子大于等于1
服务器执行执行bgsave或bgrewriteasf命令 负载因子大于等于5
收缩,负载因子小于0.1
渐进性rehash ,hash操作并非一次完成,分多次(防止服务器负载过大)
跳跃表(效率近似平衡树,且实现简单 实现 有序集合键 、集群节点作为内部数据结构)
整数集合 用于保存整数值的集合抽象数据结构 可保存int16_t int32_t int64_t整数值
typedef struct intset{
uint32_t encoding; //编码方式 值为insert_ENC_int16 、32、64
uint32_t length; //包含元素数量
int8_t contents[]; //保存元素的数组、各项在数组中按值的大小排序,无重复项
}intset;
当插入元素大于当前编码方式的时候会进行升级扩容 提升灵活性、尽可能节约内存
不支持降级
压缩列表(列表键、哈希键的底层实现之一)
一个列表建只包含少量列表项,则会用压缩列表实现
4(记录占用内存字节数)+4(记录连尾节点到起始节点有多少字节)+2(节点数量)+
节点。。。+特殊值(标记压缩列表末端)
节点 上一个节点长度prelength+编码+content数组
连锁扩展 当节点长度超过 prelength 所有节点的 prelength 会连锁扩容
对象类型与编码
String 、list、hash、set、有序集合对象
String 编码可以是int 、raw、embstr
类型检查与命令多态
1.可以对所有类型使用
del、expire、rename、type、object、dbsize等
2.只能对特定对象使用
String set、mset、get、append、strlen等
hash hdel、hset、hget、hlen等
list rpush、lpop、linsert、llen等
set sadd、spop、sinsert、scard等
zset zadd 、zcard、zrank、zscore等
内存回收:引用计数 int refcount; 创建新对象时初始化为1,为0时会被回收
对象共享 包含值int的字符串对象、嵌套字符串对象(linkedlist、hashtable、zset、set)
为字符串的不共享、验证操作浪费CPU
LRU
对象空转时长 lru属性 记录最后一次被访问时间,超过maxmemory旧的被优先释放
#redisServer.db存放数据库 redisServer。dnum存放数量
切换数据库 select 数据库号码 默认为0号数据库
新增、更新set key value
String string、list、hashtable、set、在set、
字典保存了数据库中所有键值对(称为键空间)
删除 del key
取值 get key
清空数据库 flushdb 返回数据库键数量 dbsize 、exists、rename、keys
expire key 5 second 设置键的生存时间或过期时间 或pexporeat
TTL key 显示剩余生存时间second PTTL 毫秒显示
expores字典(过期字典)存放了所有键过期时间
persist移除过期时间
过期键删除策略
1.定时删除 设置过期时间同时创建定时器,定时器过期时立马删除(内存友好,cpu紧张不建议使用)
2.惰性删除 不管过期,每次取数据都检测键是否过期(内存不友好,可能内存泄漏)
3.定期删除 每隔一段时间删除(合理设置)
AOF持久化、RDB持久化和复制功能对过期键的处理
生成RDB文件
执行save、bgsave会创建一个新的RDB文件,过期键不会保存到RDB中
RDB文件载入:
主服务器模式运行:RDB文件中未过期的键会载入,过期忽略
从服务器:所有键值对都会载入。主服务器进行数据同步时,从服务器数据库会被清空
AOF文件写入
以AOF持久化模式运行,数据库某个键过期,但是没被删除,那么AOF文件不会因为这个过期
键有任何影响,当被删除以后程序会向AOF文件追加一个DEL命令,来显式记录该键已删除
AOF重写
过期键不会保存
复制
服务器运行在复制模式下,从服务器的过期删除动作由主服务器控制,从服务器不会处理过期
键,会像正常键一样对待,接受到主服务器DEL命令才会删除
数据库通知
让客户端通过订阅给定的频道或模式来获知数据库中键的变化,已经命令执行情况
RDB持久化
非空数据库以及他们的键值统称为数据库状态
save会阻塞Redis服务器进程
bgsave会派生子进程不阻塞服务器进程(执行期间save bgsave bgrewriteaof会被拒绝)
RDB载入,在服务器启动时自动扫描执行(若开启了AOF,优先载入AOF)
save 100 10 //距离上次成功备份已经100秒内执行至少10次修改,则自动间隔性备份(即执行bgsave)
dirty计数器:记录上一次成功执行save命令后,对数据库状态进行了多少次修改
lastsave属性:记录上一次修改时间
周期性操作函数serverCron默认每隔100毫秒检测一次save选项所设置保存条件是否满足
RDB文件结构
5字符 4字节 1字节 8字节无符号整数
REDIS + db_version+ database + EOF + checknum
判定是否RBD文件 selectdb+dbnumber+key_value_pairs(1+1、2、5+...)
key_value_pairs = (expiretime_ms 1+ms 8)type +key+value
value = encoding+integer //其中字符串小于等于20字节,原样保存,大于20压缩后保存
AOF通过保存服务器所执行的写命令来记录数据库状态
命令追加(放入aifbuf)+Aof文件写入与同步
aof重写 当命令过多时,需要重写,如一个对象的操作会合并成一条语句
事件类型
1.当套接字变得可读时(客户端执行write、或close)或则有新的应答acceptable
套接字产生read事件
2,套接字变得可写时(客户端执行read),产生write事件
redis 数据结构
String set get del 可对数值自增自减
mset key value [k v .......]
decr keyname incr keyname key对应的value自增1
incrby keyname count value+=count decrby
incrbyfloat
append key-name value 追加value到 key的value的末尾
getrange key-name x y 取x到y范围内的所有值,包括xy
setrange key-name offset value 从start开始偏移量设置子串为指定值
getbit key-name offset 字节串看做二进制串,获取串中偏移为offset的二进制位的值
setbit key-name offset value
bitcount key-name [start end] 统计二进制串的1的个数
bittop operation dest-key key-name [key-name ...] 对一个或多个二进制串执行
and or xor not 操作
List
lpush入左 rpush lpop rpop eg: rpush key-name item [,item...]
lindex key-name offset 获取指定位置元素
lrange获取给定范围所有元素 lrange list-key start end
ltrim key-name start end 对列表进行修剪,只保留start到end的元素(包含s、e)
blpop key-name、... timeout 阻塞获取时延为timeout
brpop
rpoplpush list1 list2 从list1右边出栈一个元素,入栈到list2最左端
brpoplpush list1 list2 timeout 带时延的
HASH 可对value数值自增自减
hset
hmset hash-key sub-key1 value1 [sub-key2 value2 ... ]
hmget hash-key key [key1 ,...]
hlen hash-key
hdel hash-key key [key1 ,...]
hexists hash-key key
hkeys hash-key 获取所有键
hvals hash-key 获取所有值
hgetall hash-key 获取所有键值
hincrby hash-key key count
hincrbyfloat
SET
sadd key-name item、... 添加
srem key-name item、... 移除
sismember key-name item 是否包含该元素
scard key-name 返回集合包含元素数量
smembers key-name 获取所有元素
srandmember key-name [count] 从集合中随机返回一个元素,count为正数,返回随机元素
不会重复,为负数时会出现重复
spop key-name 随机移除一个元素
smove set1 set2 item 若set1中包含item,移除item并添加到set2中
sdiff key-name [key-name...]返回存在于第一个集合、但是不存在于其他集合的元素
sdiffstore dest-key key-name [key-name...] 将上一个操作的结果存到dest-key中
sinter key-name [key-name...] 返回同时存在于所有set的元素
sunion key-name [key-name...] 并集运算
sunionstore dest-key key-name [key-name...] 并存储
ZSET
zadd zset-key 728 member1 [score member ...]
zrem zset-key member 删除
zcard zset-key 返回有序集合包含的成员数量
zincrby zset-key member count member的分数加count
zcount zset-key min max 返回介于 min max间的成员
zrank zset-key member 获取member在集合中排名
zscore zset-key member 获取member分数
zrange zset-key 0 -l withscores
zrevrank zset-key member 返回有序集合member排名,分值由小到大
zrevrange zset-key start end [withscores] 由大到小
zrangebyscore zset-key min max [withscores] [limit offset count]
(获取指定分数范围的元素)
zrevrangebyscore zset-key min max [withscores] [limit offset count]
由大到小
zremrangebyrank zset-key start end 删除按排名
zremvrangebyscore zset-key min max 删除按分数
zinterstore 交 分值累和
zunionstore 并 分值取少的
subscribe channel [channel...]
unsubscribe [channel [channel...]] 退订频道,若未指定,则全部退订
publish channel message 向指定频道发送消息
psubscribe pattern [pattern...] 订阅与给定模式匹配的所有频道
punsubscribe [pattern [pattern...]] 退订与给定模式匹配的所有频道
事务 pipeline()获取事务流水线 ,然后对流水线进行以下操作
watch 监控事务要修改的键,直到exec命令期间,若被其它客户端修改,则事务失败,可选重试或放弃
unwatch
watch执行后 discard 可以清空命令队列
multi 标记事务开始
中间接收到的所有命令都会放入队列,直到执行完
exec 标记结束 在这个命令前输入的所有命令不会执行
非事务也可以通过pipeline提高执行效率,减少了通信往返次数
数据安全与性能保证
RDB(快照) bgsave save
若备份途中系统崩溃,则丢失所有数据
redis接收到shutdown命令时,会自动执行save,完毕后关闭
当服务器连接到另外一台redis服务器时,并向对方发送sync命令来开始一次复制操作
的时候,如果主服务器目前没有在执行bgsave操作,或并非刚刚执行完bgsave,那么
主服务器会执行bgsave。
由OS决定写入,但是会导致阻塞性能降低
AOF(append only file) 频率 always everysec no (写入缓冲区满了)
AOF 重写、压缩 bgrewriteaof
复制 主从服务器之间数据同步
slaveeof [host port] 设置从服务器
1.使用的配置设置,从服务器启动会先载入自己的 备份文件,然后进行同步,操作如下
2.使用命令的话,从服务器会直接尝试连接主服务器,成功后过程相同
PS:主从服务器进行初始连接时,数据库中所有数据会丢失并替换为主服务器的数据
不支持 主 主 复制
主从链::
从服务器可以拥有从服务器,唯一的区别在于,从服务器进行步骤4时,从服务器的从服务器连接会断开。
验证主服务器的aof写命令是否发送到从服务器,用户需要在向主服务器写入真正数据后,再向主服务器写入一个唯一虚构值,
通过从服务器是否存在虚构值来判定数据是否已经到达从服务器。
检测info命令输出结果的aof_pending_bio_fsync属性是否为0,为0表示服务器已经把所有数据保存到硬盘了
redis-benchmark 获取redis服务器性能特征
分布式锁的构建(watch【乐观锁】有时候不能满足需求,并且会导致性能问题)
处理线程生成唯一的uuid的identifier,传入 setnx setex 中 设置独占锁 限时锁可以通过设置uuid的有效期expire来实现
信号量
生成标识符identifier,清理过期标识符,新的标识符加入有序集合,检测新的标识符在集合中的排名,检测是否成功获取,失败则删除identifier