Redis MongoDB

作者: 小程有话说 | 来源:发表于2017-04-22 22:04 被阅读71次

Redis/MongoDB都是很棒的NoSql数据库,与Mysql之类的关系型数据库有很大的区别,在某些情况下使用它们是更好的选择。

Redis设计与实现

在读这本书之前,我不了解Redis中字典、列表数据结构,不了解Redis如何进行持久化,不了解Redis如何内存回收,不了解Redis事务特点,不知道Redis都有哪些特性;读了这本书,这些就都知道了。_

其实还有一个挺有意思的点是,程序之前底层设计都是想通的,Redis中字典和Java中HashMap很相似,字典扩容机制和ConcurrentHashMap又有异曲同工之妙;Redis内存回收也是通过引用计数来实现的。

Redis数据结构

Redis 字符串 SDS

Java中String每次修改String对象,都会创建一个新的对象,String对象具有不变性,好处是线程安全,缺点是效率较低。

Redis中字符串SDS实现方法类似StringBuffer,每次会分配多一些空间,在操作字符串时不需要新建对象,同时由于Redis单线程特性不需要考虑并发问题。

SDS结构是:free(int),len(int),buf(Byte[])

free代表空闲字节位数;len代表存储字节位数。

Redis 链表

typedef struct listNode {
    struct listNode *prev;
    struct listNode *next;
    void *value;
}

Redis中列表键、发布和订阅、慢查询、监视器等都是由链表实现的。

Redis 字典

在实现上和Java中HashMap类似,数据结构是数组+链表,当set一个key - value时,先计算key hashcode值然后找到数组对应位置插入;如果该位置已经有值 - 键冲突,那么把数组原有位置的节点赋值给新插入节点的next指针,然后插入新节点到数组。

哈希表的扩展:和Java类似,哈希表有一个负载因子,当 哈希表保存节点数量 / 哈希表大小 > 负载因子 时进行扩容。

渐进式rehash:如果hash表非常大,那么如果一次性的rehash那么服务器计算量很大,一种折中的方法就是渐进式的操作,在每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定操作以外,还会顺带把老哈希表对应的键值移动到新哈希表中,最终在某个时间,全部移动完毕;这种设计思路和Java中ConcurrentHashMap是一致的。

Redis 跳跃表

一种有序数据结构,平均时间复杂度O(longN),实现Redis中有序集合。

至于跳跃表是如何跳跃的,请看下图。


图1
图2

Redis对象

字符串对象、列表对象、哈希对象、集合对象和有序集合对象。

有序集合同时使用跳跃表和字典来实现,使用跳跃表来降低范围型操作的耗时;使用字典表来降低查找设值的耗时。

Redis内存回收

通过引用计数来实现内存回收机制,和Java的垃圾回收机制思想是类似的。

Redis实现

Redis服务器默认会创建16个数据库;客户端默认连接的是0号数据库。

Redis键过期时间实现

当给某个键设置过期时间时,Redis会在一个字典中添加key - value,key是键;value是过期时间。当下次访问这个键时先判断键是否过期。

Redis过期键删除策略

  1. 定时删除:在设置过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。
  2. 惰性删除:放任键过期不管,但是每次从键空间获取键时,都检查取得的键是否过期,如果过期,就删除。
  3. 定期删除:每隔一段时间,程序对数据库进行一次检查,删除里面过期键。

定时删除任务缺点是对CPU不友好,在过期键较多时,会占用CPU相当多一部分时间,优点是对内存友好;惰性删除任务是对CPU友好,内存不友好;定期删除是对两种方式做了个折中。

Redis使用惰性删除和定期删除,在合理使用CPU时间和避免浪费内存空间之间取得平衡。

Redis RDB持久化

save 900 1 900秒内执行最少1次修改
save 300 10 300秒内执行最少10次修改
save 60 10000 60秒内执行最少10000次修改

满足条件后,执行BGSAVE操作,RDB文件是一个经过压缩的二进制文件,由多个部分组成。

AOF持久化

AOF持久化通过保存Redis服务器所执行的写命令来记录数据库状态的。

这样做优点是不会丢失数据,缺点是随着时间流逝,AOF文件会越来越大;所以就有了AOF重写功能,对AOF文件进行缩减。

Redis ServerCron函数

ServerCron函数让我想到了Linux下crontab,这个函数默认每隔100毫秒执行一次,其任务是更新服务器状态信息,处理服务器接收到的SIGTERM信号,管理客户端资源和数据库状态,检查并执行持久化操作等等。

发布与订阅

Redis把对某个主题订阅的客户端以链表形式保存。

struct redisServer {
    list *pubsub_patterns;  // 保存所有模式订阅关系
}
typedef struct pubsubPattern {
    redisClient *client; // 订阅模式的客户端
    robj *pattern; // 被订阅的模式
} pubsubPattern;

事务

当一个Redis客户端执行命令MULTI后,当执行SET等命令时,Redis服务器并不会立刻执行而是把命令放入一个队列中,等EXEC时统一执行命令。

事务ACID性质:原子性(Atomicity),一致性(Consistency),隔离性(Isolation),持久性(Durability)。

无论Redis服务器运行在哪种持久化模式下,事务执行中途发生停机都不会影响数据库一致性。

Lua脚本

Redis客户端可以使用Lua脚本,直接在服务端原子地执行多个Redis命令。

注意:Redis主从也会把Lua脚本复制到从库中。

Redis还能进行监控慢查询等其他功能,功能很强大。

相关文章

网友评论

    本文标题:Redis MongoDB

    本文链接:https://www.haomeiwen.com/subject/cuqezttx.html