用的黑马程序员redis课程的目录,但是不想听讲了。后续都是用gpt文档获取的。
1.课程介绍(Av766995956,P145)
2.Redis数据结构-动态字符串(Av766995956,P146)
sds 1M是个界限
其实他是个由c语言实现的结构体
有这么几个参数
len
alloc
flag
char[]
len是实际长度
alloc是申请的长度
flag是标志 对应不同进制
char[]是存储的字符 最后加一个\0
为什么他是相对于原来c语言 字符串更安全了呢
因为他计算长度不需要再去遍历了,直接通过len就可以返回。
还有就是他通过alloc可以来判断是否需要扩容。
小于1M的时候,2倍+1(加上那个\0)
大于1M的时候,就是1M+1.。
3.Redis数据结构-intset(Av766995956,P147)
下面是关于intset每个特点的实现细节:
-
有序性:intset中的整数值是按照从小到大的顺序排列的。这是通过在插入新元素时,将其按照正确的位置插入,并保持整个intset有序来实现的。
-
唯一性:intset不允许存在重复的整数值。在插入新元素时,会进行唯一性检查,如果要插入的元素已经存在于intset中,则不会再次插入。
-
分段存储:intset将整数值分段存储,每个分段都有自己的编码方式(16位、32位或64位)。根据整数值的大小,将其放入适当的分段中。
-
紧凑存储:intset采用紧凑的存储方式,节省内存空间。它使用变长编码方式来存储整数值,根据整数值的范围选择合适的编码方式,并将整数值以二进制形式存储。
-
快速查找:由于intset是有序的,可以使用二分查找算法在intset中快速查找某个整数值。这样可以减少查找的时间复杂度,提高查找效率。
-
集合操作支持:intset支持常见的集合操作,比如并集、交集和差集。这是通过对多个intset进行操作,并根据需要生成新的intset来实现的。
这些实现细节使得intset成为适用于存储整数值的高效数据结构。通过理解这些细节,你可以更好地利用intset在Redis中进行数据存储和操作。如有更多问题,请随时提问。
4.Redis数据结构-Dict(Av766995956,P148)
DICT是Redis中的字典数据结构,它是用于存储键值对的高效数据结构。以下是关于DICT的实现细节,按照上述模式介绍:
-
键值对存储:DICT以键值对的形式存储数据。每个键都是唯一的,而值可以是任意类型的数据。
-
哈希表实现:DICT的底层实现是哈希表(hash table),使用哈希算法将键映射到哈希槽(hash slot)上,实现快速的键值查询和插入。
-
动态扩容:DICT具有自动动态扩容的能力,当字典中的键值对数量达到一定阈值时,自动触发扩容操作,以保证字典的性能和空间效率。
-
整数索引和哈希值索引:DICT在哈希表中维护了键的整数索引和哈希值索引。整数索引用于快速比较键是否相同,而哈希值索引用于快速定位键在哈希表中的位置。
-
冲突解决:由于哈希算法的特性,不同的键可能映射到相同的哈希槽上,导致冲突。DICT通过链地址法(chaining)来解决冲突,即在同一哈希槽上的键值对使用链表进行存储。
-
压缩列表优化:对于小规模的键值对,DICT还可以使用压缩列表(ziplist)进行存储,以节省内存空间。
-
迭代器支持:DICT支持通过迭代器遍历字典中的键值对,可以按照添加顺序或者哈希表顺序进行迭代。
这些实现细节使得DICT成为Redis中常用的数据结构之一,具有高效的键值查询和插入能力。通过了解DICT的实现,可以更好地使用和优化Redis的字典数据结构。如果有更多问题,请随时提问。
5.Redis数据结构-Dict的渐进式rehash(Av766995956,P149)
Redis的渐进式rehash是通过以下步骤实现的:
-
创建新哈希表:在进行字典扩容时,先创建一个新的空哈希表,作为新哈希表。
-
将字典的rehashidx属性设置为0:rehashidx是一个索引,用于记录当前正在进行rehash的位置。将其初始化为0,表示从字典的第一个哈希槽开始进行rehash。
-
渐进式迁移键值对:在每次字典操作时,Redis会检查rehashidx的值。如果rehashidx小于旧哈希表的大小,则表示仍有键值对需要迁移。
-
迁移一小部分键值对:Redis会根据rehashidx的位置,将旧哈希表上对应哈希槽中的键值对逐个迁移到新哈希表中。迁移的数量可以根据配置或自动调整。
-
更新rehashidx属性:每次完成一小批键值对的迁移后,将rehashidx属性递增,指向旧哈希表中的下一个哈希槽。
-
重复步骤3-5:重复以上步骤,直到rehashidx超过旧哈希表的大小,表示所有键值对都已迁移完毕。
-
替换旧哈希表:当所有键值对都已迁移至新哈希表后,将新哈希表替换为字典的当前哈希表,并释放旧哈希表的内存。
通过上述步骤,Redis能够在字典仍然使用的情况下,逐步迁移键值对到新的哈希表中,实现渐进式rehash。这种方式保证了字典的可用性和性能,而不会对系统造成明显的停顿或延迟。
dict作为Redis的底层数据结构,在Redis中被广泛应用。它被用于存储Redis中的键、值以及其他元数据,如哈希类型的字段和值、有序集合的成员和分值等。
在Redis中,哈希类型的键值对实际上也是存储在dict中的。当你使用Redis的哈希类型数据结构(例如HSET、HGET等命令)时,Redis会在底层使用一个dict来存储这些键值对。这样可以高效地实现哈希类型的操作,比如获取、设置、删除一个字段等。
因此,可以说Redis中的dict和hashmap之间存在一种嵌套的关系,dict可以看作是Redis对hashmap的一种封装和扩展。dict提供了高性能、高扩展性的键值对存储能力,并为Redis的其他数据结构提供了基础支持。
6.Redis数据结构-ZipList(Av766995956,P150)
-
紧凑存储:ZipList使用连续的内存空间来存储多个元素,占用的内存空间相对较少。
-
高效访问:由于元素在连续的内存中存储,所以可以通过索引进行快速随机访问。
-
支持插入、删除和更新操作:ZipList可以在两端进行元素的插入、删除和更新操作,使得操作更加灵活。
-
支持特殊元素:ZipList可以存储特殊类型的元素,如整数或小于等于12字节的字符串,这些特殊元素不需要额外的前缀长度字段,进一步减少内存占用。
-
压缩操作:ZipList支持自动调整内存大小,可以在插入或删除操作后进行压缩,减少内存的浪费。
-
适用于小型列表:ZipList在存储小型列表时表现良好,对于长度较长的列表或需要频繁插入、删除操作的情况,则可能不太适合。
总的来说,ZipList是一种高效的、占用较少内存的数据结构,适用于小型列表的存储和操作,可以在一些场景中提供更好的性能。
7.Redis数据结构-ZipList的连锁更新问题(Av766995956,P151)
在Redis中,ZipList数据结构存在一个称为“连锁更新(chaining)”的问题。这个问题在进行插入或删除操作时可能会导致整个ZipList的重建,造成性能下降。
当一个元素插入或删除时,如果插入或删除操作引起了某个元素的大小变化,而该元素又位于其他元素的前缀长度字段之后,那么这些前缀长度字段都需要被更新。这样的更新过程可能会导致链式更新的发生,即一个长度的更新又引发了另一个长度的更新,依次类推,直到整个ZipList都被重建。
这个连锁更新的问题会导致插入或删除操作的时间复杂度变为O(N),其中N是ZipList的长度,而不是常数时间复杂度。这会对性能产生明显的影响,特别是在处理较长的ZipList时。
为了避免连锁更新问题,可以采取以下策略:
-
避免频繁的插入和删除操作:如果ZipList的更新操作相对较少,那么连锁更新问题的影响也会相对较小。
-
使用其他数据结构:对于需要频繁的插入和删除操作,可以考虑使用其他数据结构,如LinkedList或者Redis的其他数据结构,如List或SortedSet等。
-
定期重建ZipList:如果连锁更新问题无法避免,可以定期进行ZipList的重建操作,以恢复其性能。可以使用Redis的命令ZRANGE和ZADD等操作将数据从ZipList转移到其他数据结构中,然后再转回ZipList。
虽然ZipList存在连锁更新问题,但对于合理使用的小型列表,其优势仍然是显著的。对于长度较长的列表或需要频繁插入、删除操作的情况,建议考虑使用其他适合的数据结构。
8.Redis数据结构-QuickList(Av766995956,P152)
Redis的数据结构之一是QuickList。QuickList是一种双向链表和压缩列表的组合,用于存储多个有序列表(包括列表、集合和有序集合)。
QuickList的设计目标是兼顾读写性能和内存占用。它将多个有序列表连接在一起,每个有序列表由一个双向链表或压缩列表表示。这种设计使得QuickList可以在读操作中保持较高的性能,同时在写操作中减少内存占用。
QuickList的特点如下:
-
分层结构:QuickList由多个列表节点组成,每个列表节点维护一个有序列表。每个列表节点可以是一个双向链表节点或压缩列表节点。这种分层结构可以提供快速的插入、删除和更新操作。
-
空间效率:QuickList通过使用压缩列表来节省内存空间。压缩列表是一种紧凑的数据结构,能够在较少的内存空间中存储多个元素。
-
连续访问:QuickList的连续访问操作效率较高。通过使用双向链表,可以在常数时间内访问节点的前驱和后继节点。
-
快速插入和删除:QuickList通过在节点之间动态分配和释放空间,实现了快速的插入和删除操作。
-
压缩和分割:QuickList支持压缩和分割操作。压缩可以合并相邻的小列表节点,减少内存占用。分割可以将一个节点分成两个节点,提高插入和删除操作的性能。
-
迭代器支持:QuickList提供了迭代器支持,可以方便地遍历列表节点和元素。
QuickList适用于存储大量有序列表的场景,尤其是在需要频繁进行插入、删除和更新操作时。它在读操作中具有较高的性能,并且能够有效地利用内存空间。
9.Redis数据结构-SkipList(Av766995956,P153)
Redis的数据结构之一是SkipList(跳跃表)。SkipList是一种有序的数据结构,用于快速查找和插入操作。
SkipList的特点如下:
-
高效的查找操作:SkipList通过使用多级索引来加速查找操作。通过在每个级别创建指向下一个级别的指针,SkipList可以在平均情况下以O(log n)的时间复杂度进行查找。
-
简单的实现:SkipList相对于其他平衡树结构,如红黑树或AVL树,实现起来较为简单。它不需要进行平衡调整操作,并且可以通过逐层搜索来进行插入和删除操作。
-
并发性:SkipList在并发环境中表现良好。由于每个节点都是独立的,不需要对整个数据结构进行锁定,因此可以支持并发的读写操作。
-
空间效率:SkipList相对于其他树结构,具有较好的空间利用率。虽然SkipList在平均情况下需要使用额外的空间来存储索引节点,但相对于一些平衡树结构,其空间占用较低。
-
简单的迭代器:SkipList提供了简单而有效的迭代器,可以进行正向和反向遍历操作。
SkipList适用于那些需要快速查找和插入操作的场景。它在平均情况下具有较好的性能,并且相对于其他树结构更容易实现和维护。但是,SkipList不适用于那些需要频繁的删除操作,因为删除操作需要重新调整索引节点,这可能导致性能下降。
以下是对Redis中的三种列表数据结构(ZipList、QuickList和SkipList)特点的比较表格:
特点 | ZipList | QuickList | SkipList |
---|---|---|---|
存储结构 | 压缩列表 | 分层结构 | 跳跃表 |
内存占用 | 较小 | 中等 | 中等 |
插入/删除操作 | O(N) | O(1) | O(log N) |
遍历操作 | O(N) | O(N) | O(N) |
查找操作 | O(N) | O(log N) | O(log N) |
并发性 | 同一节点上的操作会导致连锁更新问题 | 适用于并发读写操作 | 适用于并发读写操作 |
空间效率 | 相对较高 | 中等 | 相对较低 |
实现复杂度 | 相对简单 | 中等 | 相对简单 |
适用场景 | 小型列表,读操作频繁,插入/删除操作相对较少 | 大型列表,插入/删除操作频繁,内存占用要求相对较高 | 快速查找和插入操作频繁,对内存占用要求相对较高的场景 |
需要注意的是,以上表格只是对这三种列表数据结构的一些主要特点进行了简要比较。在实际应用中,选择适合的数据结构应综合考虑具体场景的读写需求、内存占用、并发性等因素。
10.Redis数据结构-RedisObject(Av766995956,P154)
Redis中的数据结构是通过RedisObject表示的。RedisObject是一个通用的数据结构,可以表示字符串、列表、哈希表、集合、有序集合等不同类型的数据。
RedisObject的定义如下:
typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */
int refcount;
void *ptr;
} robj;
其中,type字段表示RedisObject的类型,encoding字段表示RedisObject的内部编码方式,lru字段用于实现LRU淘汰策略,refcount字段表示RedisObject的引用计数,ptr字段指向实际的数据。
Redis中的每个键值对都对应一个RedisObject,该键值对的键和值分别由两个RedisObject表示。键的类型通常为字符串类型,值的类型可以是字符串、列表、哈希表、集合、有序集合等。
RedisObject的内部编码方式由encoding字段表示,不同的类型有不同的编码方式。例如,字符串类型的编码方式可以是int、embstr、raw等。Redis会根据实际的数据情况选择最适合的编码方式,以节省内存空间和提高性能。
通过RedisObject,Redis可以对不同类型的数据进行高效的存储和操作。例如,对于列表类型的数据,Redis可以快速地进行插入、删除、查找等操作。对于哈希表类型的数据,Redis可以快速地进行字段的添加、删除、查找等操作。
总之,Redis通过RedisObject实现了丰富的数据结构,使得Redis具有了很强的灵活性和性能。
11.Redis数据结构-五种数据类型-String(Av766995956,P155)
在Redis中,有五种常见的数据类型,分别是String(字符串)、List(列表)、Hash(哈希表)、Set(集合)和ZSet(有序集合)。本节将详细介绍String类型。
String类型是Redis中最基本的数据类型,它可以存储任意长度的字符串。String类型的值可以是简单的字符串,也可以是整数或浮点数。
在Redis中,可以对String类型的值进行一系列的操作,如设置值、获取值、修改值、追加值等。
以下是String类型的一些常用命令和操作:
- 设置值:使用SET命令将一个键值对设置为String类型的值。
SET key value
- 获取值:使用GET命令获取一个键对应的String类型的值。
GET key
- 修改值:使用SET命令可以修改一个键对应的String类型的值,如果键不存在,将创建一个新的键。
SET key value
- 追加值:使用APPEND命令将一个字符串追加到已有的String类型的值的末尾。
APPEND key value
- 获取子串:使用GETRANGE命令获取一个String类型的值的子串。
GETRANGE key start end
其中,start和end指定子串的起始位置和结束位置。
- 获取值长度:使用STRLEN命令获取一个String类型的值的长度。
STRLEN key
- 自增和自减:使用INCR和DECR命令对一个存储整数的String类型的值进行自增和自减操作。
INCR key
DECR key
String类型在Redis中的应用非常广泛,可以用于缓存、计数器、分布式锁等多种场景。通过String类型的各种操作,可以在Redis中高效地处理各种数据。
12.Redis数据结构-五种数据类型-List(Av766995956,P156)
在Redis中,有五种常见的数据类型,分别是String(字符串)、List(列表)、Hash(哈希表)、Set(集合)和ZSet(有序集合)。本节将详细介绍List类型。
List类型是一种有序的、可以重复的字符串列表。可以将List看作是一个链表,每个节点都包含一个字符串值。Redis内部使用双向链表实现List类型。
以下是List类型的一些常用命令和操作:
- 添加元素:使用LPUSH或RPUSH命令将一个或多个元素添加到List的左侧或右侧。
LPUSH key value1 value2 ...
RPUSH key value1 value2 ...
- 获取元素:使用LRANGE命令获取List中指定范围的元素。
LRANGE key start stop
其中,start和stop是索引,表示要获取的元素的起始索引和结束索引。
- 获取长度:使用LLEN命令获取List的长度(即元素的个数)。
LLEN key
- 弹出元素:使用LPOP或RPOP命令分别从List的左侧或右侧弹出一个元素,并将其从List中删除。
LPOP key
RPOP key
- 获取元素索引:使用LINDEX命令获取List中指定索引的元素。
LINDEX key index
- 修改元素:使用LSET命令将List中指定索引的元素替换为新值。
LSET key index value
- 删除元素:使用LREM命令从List中删除指定数量的元素。
LREM key count value
其中,count为正数时表示从左到右删除count个值为value的元素,为负数时表示从右到左删除count个值为value的元素。
List类型在Redis中的应用非常广泛,可以用于实现消息队列、任务队列、最新消息列表等场景。通过List类型的各种操作,可以方便地对列表进行添加、删除、获取等操作。
13.Redis数据结构-五种数据类型-Set(Av766995956,P157)
在Redis中,Set是一种无序的、不重复的字符串集合。Set类型的值可以包含多个字符串元素,并且这些元素都是唯一的,即不允许重复。Set类型在Redis内部使用哈希表实现。
以下是Set类型的一些常用命令和操作:
- 添加元素:使用SADD命令将一个或多个元素添加到Set中。
SADD key member1 member2 ...
- 获取元素:使用SMEMBERS命令获取Set中的所有元素。
SMEMBERS key
- 获取集合大小:使用SCARD命令获取Set的大小,即元素的个数。
SCARD key
- 判断元素是否存在:使用SISMEMBER命令判断一个元素是否存在于Set中。
SISMEMBER key member
如果member存在于Set中,则返回1;否则返回0。
- 移除元素:使用SREM命令从Set中移除指定的元素。
SREM key member1 member2 ...
- 随机获取元素:使用SRANDMEMBER命令随机获取Set中的一个或多个元素。
SRANDMEMBER key [count]
如果count大于0,则返回count个随机元素;如果count小于0,则返回count绝对值个不重复的随机元素。
- 获取交集、并集和差集:使用SINTER、SUNION和SDIFF命令分别获取多个Set之间的交集、并集和差集。
SINTER key1 key2 ...
SUNION key1 key2 ...
SDIFF key1 key2 ...
Set类型在Redis中的应用场景很多,比如用于存储用户的标签、记录点赞或收藏等操作的用户ID等。通过Set类型的各种操作,可以方便地对集合进行添加、删除、判断元素是否存在等操作。
14.Redis数据结构-五种数据类型-ZSet(Av766995956,P158)
在Redis中,ZSet(有序集合)是一种有序的、不重复的字符串集合。与Set类型不同的是,ZSet中的每个元素都会关联一个分数(score),用于对元素进行排序。ZSet内部使用跳跃表(Skip List)和哈希表的结合来实现。
以下是ZSet类型的一些常用命令和操作:
- 添加元素:使用ZADD命令将一个或多个元素添加到ZSet中,并指定每个元素的分数。
ZADD key score1 member1 score2 member2 ...
- 获取元素:使用ZRANGE命令按照元素的分数从小到大获取指定范围的元素。
ZRANGE key start stop [WITHSCORES]
如果指定了WITHSCORES参数,则同时返回元素的分数。
- 获取元素个数:使用ZCARD命令获取ZSet中的元素个数。
ZCARD key
- 获取指定元素的分数:使用ZSCORE命令获取指定元素的分数。
ZSCORE key member
- 获取指定范围内元素的个数:使用ZCOUNT命令获取指定范围内元素的个数。
ZCOUNT key min max
其中,min和max分别表示分数的最小值和最大值。
- 修改元素的分数:使用ZINCRBY命令增加或减少指定元素的分数。
ZINCRBY key increment member
其中,increment表示要增加或减少的分数值。
- 移除元素:使用ZREM命令从ZSet中移除指定的元素。
ZREM key member1 member2 ...
ZSet类型在Redis中的应用场景很多,比如用于实现排行榜、计算帖子热度、按照时间排序的消息队列等。通过ZSet类型的各种操作,可以方便地对有序集合进行添加、删除、获取元素等操作,并且可以根据分数进行排序和范围查询。
15.Redis数据结构-五种数据类型-Hash(Av766995956,P159)
在Redis中,Hash(哈希)是一种用于存储键值对的数据结构。在Hash中,键值对被存储在一个哈希表中,其中键是唯一的,而值可以是字符串、数字或其他数据类型。Hash在Redis内部使用哈希表来实现。
以下是Hash类型的一些常用命令和操作:
- 添加或修改键值对:使用HSET命令将键值对添加或修改到Hash中。
HSET key field value
其中,key表示Hash的键,field表示字段名,value表示字段值。
- 获取字段值:使用HGET命令获取指定字段的值。
HGET key field
- 获取所有字段和值:使用HGETALL命令获取Hash中的所有字段和值。
HGETALL key
返回结果是一个包含所有字段和值的列表,例如:[field1, value1, field2, value2, …]
- 获取所有字段:使用HKEYS命令获取Hash中的所有字段。
HKEYS key
返回结果是一个包含所有字段的列表。
- 获取所有值:使用HVALS命令获取Hash中的所有值。
HVALS key
返回结果是一个包含所有值的列表。
- 获取字段数量:使用HLEN命令获取Hash中字段的数量。
HLEN key
- 删除字段:使用HDEL命令从Hash中删除指定的字段。
HDEL key field1 field2 ...
Hash类型在Redis中的应用场景很多,比如用于存储用户信息、缓存对象、存储配置信息等。通过Hash类型的各种操作,可以方便地对键值对进行添加、修改、获取和删除等操作,适用于存储结构化的数据。
使用场景总结
以下是各个数据结构在Redis中常见的使用场景总结表:
数据结构 | 使用场景 |
---|---|
String | 存储单个值、计数器、缓存对象等 |
List | 消息队列、任务队列、最新动态等 |
Set | 标签、好友关系、共同兴趣等 |
Sorted Set | 排行榜、计分板、有序消息队列等 |
Hash | 用户信息、缓存对象、配置信息等 |
Bitmap | 用户签到、在线状态、布隆过滤器等 |
HyperLogLog | 基数统计、独立访客统计等 |
Geospatial | 地理位置信息、附近的人等 |
Stream | 消息发布与订阅、事件流处理等 |
以上列举了Redis中常见数据结构的使用场景,每个数据结构都具有不同的特点和适用范围。根据具体需求,选择合适的数据结构可以有效地解决问题并提高性能。
16.Redis网络模型-用户空间与内核空间(Av766995956,P160)
用户空间是指Redis服务器进程在操作系统中运行的部分,它是Redis的主要执行环境。在用户空间中,Redis服务器处理客户端发来的请求、执行命令、读写数据等操作。用户空间可以直接访问应用程序的内存和资源,因此具有较高的灵活性和自由度。
内核空间是指操作系统内核中运行的部分,它负责处理底层的网络通信、文件系统、设备驱动等底层操作。在网络通信中,Redis服务器通过系统调用将网络数据发送到内核空间,并通过内核空间来接收和处理网络数据。内核空间负责底层网络协议的处理,如TCP/IP协议栈,以及网络设备的管理和调度。
用户空间和内核空间之间通过系统调用来进行通信和协作。Redis服务器在用户空间中发送网络数据时,会通过系统调用将数据传递给内核空间的网络协议栈,由内核负责将数据发往网络。类似地,当内核空间接收到网络数据时,会通过系统调用将数据传递给Redis服务器的用户空间,由Redis服务器处理和解析网络数据。
总而言之,用户空间和内核空间在Redis的网络模型中扮演了不同的角色,用户空间是Redis服务器的主要执行环境,处理客户端请求和命令操作,而内核空间负责底层的网络通信和协议处理。它们通过系统调用进行通信和协作,实现了Redis的网络功能。
17.Redis网络模型-阻塞IO(Av766995956,P161)
18.Redis网络模型-非阻塞IO(Av766995956,P162)
19.Redis网络模型-IO多路复用(Av766995956,P163)
20.Redis网络模型-IO多路复用之select(Av766995956,P164)
21.Redis网络模型-IO多路复用之poll(Av766995956,P165)
22.Redis网络模型-IO多路复用之epoll(Av766995956,P166)
言简意赅版
-
阻塞IO:在进行输入输出操作时,如果数据没有准备好或无法立即发送,程序会一直等待,导致线程阻塞,无法进行其他任务。
-
非阻塞IO:在进行输入输出操作时,即使数据没有准备好或无法立即发送,程序也会立即返回,不会阻塞线程,可以继续进行其他任务。
-
IO多路复用:通过一种机制,允许程序同时监听多个IO事件,以提高IO操作的效率和性能。
-
IO多路复用之select:一种基于轮询的IO多路复用技术,通过select系统调用,可以同时监听多个文件描述符的读写事件,但是存在文件描述符数量限制和效率问题。
-
IO多路复用之poll:一种基于轮询的IO多路复用技术,通过poll系统调用,可以同时监听多个文件描述符的读写事件,相较于select,没有文件描述符数量限制,但效率问题仍然存在。
-
IO多路复用之epoll:一种基于事件驱动的IO多路复用技术,通过epoll系统调用,在事件就绪时通知应用程序,避免了轮询和效率问题,是目前最常用且效率最高的IO多路复用技术。
简单来说,阻塞IO会导致线程等待,无法进行其他任务,非阻塞IO可以立即返回,不会阻塞线程。IO多路复用是一种提高IO操作效率的机制,其中select、poll和epoll是常用的IO多路复用技术,其中epoll是目前使用最广泛且效率最高的一种。
生动形象版
假设你正在等待朋友的到来,而你手上还有其他事情要处理。下面我将使用生动的比喻来解释阻塞IO、非阻塞IO和IO多路复用的概念:
-
阻塞IO就像你坐在门口等待朋友的到来,你不做任何其他事情,直到朋友出现。如果朋友迟迟不来,你就一直等着,无法做其他事情。这种方式效率低下,浪费了大量的时间。
-
非阻塞IO就像你坐在门口等待朋友的到来,但是你手上还有其他事情要处理。你不断地张望,如果朋友没有出现,你可以继续处理其他事情。你会时不时地看看门口,如果朋友出现了,你就立即去迎接。这种方式让你能够同时处理其他任务,不会浪费太多时间。
-
IO多路复用就像你在门口放上一个门铃,同时你手上还有其他事情要处理。你不需要一直盯着门口,而是继续做其他事情。当朋友按下门铃时,你会立即收到通知,然后去迎接朋友。这种方式让你能够同时处理其他任务,不需要反复检查门口,而只需要在有事件发生时被动地接收通知。
-
IO多路复用之select就像你不断地轮流看门口是否有朋友出现,每次只能看一个门口。如果朋友在其中一个门口出现,你就立即去迎接。但是,当门口数量很多时,你需要不断地切换,造成一定的开销。
-
IO多路复用之poll就像你拥有一批门口,但是你只能一个一个地看,无法同时观察所有门口。当朋友在某个门口出现时,你立即去迎接。相较于select,你可以同时观察更多的门口,但仍然需要一个个地检查。
-
IO多路复用之epoll就像你拥有一批门口,并且每个门口都有一个信号灯,只有当朋友出现时,对应的门口的信号灯才会亮起。你只需要关注亮起的信号灯,这样你就可以知道哪个门口有朋友出现,然后去迎接。相较于select和poll,epoll更加高效,省去了不必要的轮询和切换开销。
23.Redis网络模型-epoll的ET和LT模式(Av766995956,P167)
24.Redis网络模型-基于epoll的服务端流程(Av766995956,P168)
25.Redis网络模型-信号驱动IO及异步IO(Av766995956,P169)
晦涩模式
-
epoll的ET和LT模式:
- ET(边缘触发)模式:在ET模式下,只有当文件描述符上的IO事件状态发生变化时,才会触发通知。也就是说,只有在文件描述符从无事件状态变为有事件状态时,epoll才会通知应用程序。适用于高性能需求的场景,但需要注意处理数据的完整性。
- LT(水平触发)模式:在LT模式下,只要文件描述符上有IO事件存在,就会不断地通知应用程序,直到应用程序处理完事件。适用于一般的场景,不会漏掉事件,但需要应用程序主动处理事件。
-
基于epoll的服务端流程:基于epoll的服务端流程通常包括以下步骤:
- 创建socket并绑定端口。
- 将socket设置为非阻塞模式。
- 创建epoll实例,将监听的socket添加到epoll的事件集合中。
- 进入事件循环,等待epoll通知。
- 当有新连接到来时,处理连接请求,将新连接的socket添加到epoll的事件集合中。
- 当有数据可读时,处理读事件,读取数据并进行相应的业务逻辑处理。
- 当有数据可写时,处理写事件,将数据发送给客户端。
- 循环处理事件,直到退出。
-
信号驱动IO:信号驱动IO是一种异步IO的方法,通过注册信号处理函数,在IO操作完成时,操作系统会发送一个信号给应用程序,应用程序在信号处理函数中处理完成的IO数据。这种方式相对于阻塞IO和非阻塞IO,可以在IO操作进行的同时继续处理其他任务,但可能会引发信号处理函数的竞争条件等问题。
-
异步IO:异步IO是一种IO模型,在进行IO操作时,应用程序发起IO请求后立即返回,而不需要等待IO操作的完成。当IO操作完成时,操作系统通知应用程序,应用程序进行相应的处理。这种方式相对于其他IO模型,可以更高效地利用系统资源,但需要操作系统和应用程序的支持。
-
REDIS的网络IO模式:REDIS使用了一种基于事件驱动的IO多路复用技术,主要分为以下两种模式:
- 同步阻塞模式(Sync Blocking):每个客户端请求到达时,服务器会阻塞地处理请求,直到请求完成并返回结果给客户端。这种模式适用于连接数较少的场景,但在高并发情况下效率较低。
- 异步非阻塞模式(Async Non-Blocking):服务器使用单独的线程来处理IO事件,当有新的请求到达时,将其加入到事件队列中,然后由工作线程异步地处理请求。这种模式适用于高并发场景,可以提高并发处理能力和系统的响应速度。
希望上述解释对您有帮助。
简明模式
好的,我会尽量用简单的语言来解释。以下是对您提到的概念的简明解释:
-
epoll的ET和LT模式:ET模式只在IO事件状态发生变化时通知应用程序,LT模式在IO事件存在时一直通知应用程序。
-
基于epoll的服务端流程:创建socket并绑定端口,把socket设置为非阻塞模式,创建epoll实例并将监听的socket添加到其中,然后进入事件循环,等待epoll通知。当有新连接到来时,处理连接请求并将新连接的socket添加到epoll中。当有数据可读时,读取数据并进行业务处理。当有数据可写时,发送数据给客户端。循环处理事件直到退出。
-
信号驱动IO:在IO操作完成时,操作系统发送一个信号给应用程序,应用程序在信号处理函数中处理完成的IO数据。
-
异步IO:应用程序发起IO请求后立即返回,不需要等待IO操作完成。当IO操作完成时,操作系统通知应用程序进行处理。
-
REDIS的网络IO模式:REDIS使用了一种基于事件驱动的IO多路复用技术。它有两种模式:同步阻塞模式和异步非阻塞模式。在同步阻塞模式下,服务器会阻塞地处理每个请求。在异步非阻塞模式下,服务器使用单独的线程来处理IO事件,并异步处理请求。
希望这次的解释更加简明易懂。如果还有不清楚的地方,请随时提问。
26.Redis网络模型-Redis是单线程吗,为什么用单线程(Av766995956,P170)
- Redis网络模型:
- Redis是单线程的,它使用单线程来处理所有的客户端请求和内部操作。
- Redis之所以使用单线程,是因为它是基于内存的数据库,内存操作速度非常快,瓶颈往往在于网络和磁盘IO。通过使用单线程,避免了多线程之间的竞争和同步开销,提高了系统的整体性能。
27.Redis网络模型-Redis单线程及多线程网络模型变更(Av766995956,P171)
- Redis单线程及多线程网络模型变更:
- 在早期版本的Redis中,使用的是单线程网络模型,即一个线程处理所有的网络请求。
- 从Redis 6.0版本开始,引入了多线程网络模型。这个改变是为了更好地利用多核处理器的能力,在面对更高的并发请求时提供更好的性能。
- 在多线程网络模型中,Redis将网络IO和命令处理分离为两个线程池,网络IO线程池负责接收和发送网络数据,命令处理线程池负责处理客户端请求。
28.Redis通信协议-RESP协议(Av766995956,P172)
- Redis通信协议:RESP协议(Redis Serialization Protocol)是Redis使用的一种简单的二进制协议,用于客户端和服务器之间的通信。它定义了一些规则和格式,包括请求和响应的结构、数据类型、错误处理等。
29.Redis通信协议-基于Socket的自定义Redis客户(Av766995956,P173)
- Redis通信协议-基于Socket的自定义Redis客户端:基于Socket编程,可以开发自定义的Redis客户端。通过建立与Redis服务器的连接,发送符合RESP协议的请求,接收和解析服务器的响应,实现与Redis进行通信和交互。
30.Redis内存回收-过期key处理(Av766995956,P174)
-
Redis内存回收-过期key处理:Redis中的key可以设置过期时间,当key过期时,Redis会自动将其删除。Redis使用了定期删除和惰性删除两种策略来处理过期key。定期删除会定期地扫描一部分key,删除过期的key,但可能会导致内存占用过高。惰性删除是在客户端访问过期key时才进行删除。通过这两种策略结合,Redis可以高效地处理过期key。
31.Redis内存回收-内存淘汰策略(Av766995956,P175) -
Redis内存回收-内存淘汰策略:当Redis的内存使用达到上限时,需要对数据进行淘汰来释放内存。Redis提供了多种淘汰策略,包括LRU(最近最少使用)、LFU(最不经常使用)、随机等。这些策略根据数据的访问频率或随机选择来判断哪些数据应该被淘汰,以保证系统的稳定性和性能。
redis内存回收总结
Redis内存回收是指在Redis使用内存达到上限时,需要对数据进行淘汰或处理,以释放内存空间。下面是对Redis内存回收的总结: -
过期key处理:Redis中的key可以设置过期时间,当key过期时,Redis会自动将其删除。Redis使用定期删除和惰性删除两种策略来处理过期key。
-
定期删除:Redis会定期地扫描一部分key,删除过期的key。这种策略可以释放内存,但可能会导致内存占用过高。
-
惰性删除:当客户端访问过期key时,Redis会在访问时才进行删除。这种策略可以减少内存占用,但可能会导致一些过期key一直存在。
-
内存淘汰策略:当Redis的内存使用达到上限时,需要对数据进行淘汰来释放内存。Redis提供了多种淘汰策略,包括LRU(最近最少使用)、LFU(最不经常使用)、随机等。这些策略根据数据的访问频率或随机选择来判断哪些数据应该被淘汰,以保证系统的稳定性和性能。
通过适当配置过期时间和选择合适的淘汰策略,可以有效管理Redis的内存使用,保证系统的正常运行。