Redis设计与实现
文章平均质量分 74
explore翔
安徽某985小硕,记录日常学习生活,欢迎大家交流指教。
展开
-
redis缓存相关的问题
如果线程A删除了数据库中的值,但还没来得及删除缓存值,线程B就开始读取数据了,那么此时,线程B查询缓存时,发现缓存命中,就会直接从缓存中读取旧值。如果应用先完成了数据库的更新,但是,在删除缓存时失败了,那么,数据库中的值是新值,而缓存中的是旧值,这肯定是不一致的。这个时候,如果有其他的并发请求来访问数据,按照正常的缓存访问流程,就会先在缓存中查询,但此时,就会读到旧值了。我们假设应用先删除缓存,再更新数据库,如果缓存删除成功,但是数据库更新失败,那么,应用再访问数据时,缓存中没有数据,就会发生缓存缺失。原创 2023-03-13 16:51:27 · 295 阅读 · 0 评论 -
影响redis性能的一些潜在因素
在这一过程中,Redis就会采用写时复制机制,也就是说,一旦有数据要被修改,Redis并不会直接修改内存中的数据,而是将这些数据拷贝一份,然后再进行修改。不过,需要注意的是,在CPU的NUMA架构下,对CPU核的编号规则,并不是先把一个CPU Socket中的所有逻辑核编完,再对下一个CPU Socket中的逻辑核编码,而是先给每个CPU Socket中每个物理核的第一个逻辑核依次编号,再给每个CPU Socket中的物理核的第二个逻辑核依次编号。**在Redis实例的运行环境中,是否启用了透明大页机制?原创 2023-03-07 09:57:36 · 561 阅读 · 0 评论 -
redis数据结构的适用场景分析
1、String 类型的内存空间消耗问题,以及选择节省内存开销的数据类型的解决方案。为什么 String 类型内存开销大?图片 ID 和图片存储对象 ID 都是 10 位数,我们可以用两个 8 字节的 Long 类型表示这两个 ID。因为 8 字节的 Long 类型最大可以表示 2 的 64 次方的数值,所以肯定可以表示 10 位数。但是,为什么 String 类型却用了 64 字节呢?除了记录实际数据,String 类型还需要额外的内存空间记录数据长度、空间使用等信息,这些信息也叫作元数据。当实际保存原创 2023-03-03 14:29:03 · 400 阅读 · 0 评论 -
切片集群:数据增多了,是该加内存还是加实例?
但是,要进一步定位到实例,还需要知道哈希槽分布在哪个实例上。切片集群,也叫分片集群,就是指启动多个Redis实例组成一个集群,然后按照一定的规则,把收到的数据划分成多份,每一份用一个实例来保存。回到我们刚刚的场景中,如果把25GB的数据平均分成5份(当然,也可以不做均分),使用5个实例来保存,每个实例只需要保存5GB数据。Redis Cluster方案提供了一种重定向机制,所谓的“重定向”,就是指,客户端给一个实例发送数据读写操作时,这个实例上并没有相应的数据,客户端要再给一个新实例发送操作命令。原创 2023-03-01 10:16:20 · 143 阅读 · 0 评论 -
redis变慢如何排查
redis读很多都是O(1)的,但是范围查询这些是O(N)的,还有大于线性的,比排序等,这导致长时间占用CPU,表现就是ops不高但是CPU使用率很高。AOF重写期间会fork子进程,拷贝页表,如果页表很大,就会消耗大量CPU资源,如果重写期间出现写操作,就会写时复制,在副本上写。先在redis服务器上测试响应延迟, redis -cli --latency(ping出来的),避免网络原因。内存不够导致哈希冲突,以及内存淘汰策略。如果慢日志查询某个时间点,慢操作,有可能是key集中过期了,导致缓存雪崩。原创 2023-03-01 09:52:17 · 432 阅读 · 0 评论 -
哨兵机制:主库挂了,如何不间断服务?哨兵集群:哨兵挂了怎么办?
同样,如果主库也没有在规定时间内响应哨兵的 PING 命令,哨兵就会判定主库下线,然后开始自动切换主库的流程。哨兵其实就是一个运行在特殊模式下的 Redis 进程,主从库实例运行的同时,它也在运行。如果不想让业务感知到异常,客户端只能把写失败的请求先缓存起来或写入消息队列中间件中,等哨兵切换完主从后,再把这些写请求发给新的主库。如果从库总是和主库断连,而且断连次数超出了一定的阈值,我们就有理由相信,这个从库的网络状况并不是太好,就可以把这个从库筛掉了。如果没有出现得分最高的从库,那么就继续进行下一轮。原创 2023-02-28 10:05:01 · 389 阅读 · 0 评论 -
redis主从同步:如何实现数据一致
具体来说,从库给主库发送 psync 命令,表示要进行数据同步,主库根据这个命令的参数来启动复制。就像刚刚示意图的中间部分,主库和从库之间相差了 put d e 和 put d f 两个操作,在增量复制时,主库只需要把它们同步给从库,就行了。然后,我们可以再选择一些从库(例如三分之一的从库),在这些从库上执行如下命令,让它们和刚才所选的从库,建立起主从关系。SQL线程也是在Slave中创建的,当Slave检测到中继日志有更新,就会将更新的内容同步到Slave数据库中,这样就保证了主从的数据的同步。原创 2023-02-27 10:52:10 · 472 阅读 · 0 评论 -
AOF:redis宕机,如何避免数据丢失
AOF 重写机制就是在重写时,Redis 根据数据库的现状创建一个新的 AOF 文件,也就是说,读取数据库中的所有键值对,然后对每一个键值对用一条命令记录它的写入。而且,AOF 日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销。这样一来,即使宕机了,这个 AOF 日志的操作仍然是齐全的,可以用于恢复。和 AOF 相比,RDB 记录的是某一时刻的数据,并不是操作,所以,在做数据恢复时,我们可以直接把 RDB 文件读入内存,很快地完成恢复。原创 2023-02-27 10:09:34 · 395 阅读 · 0 评论 -
redis为什么用单线程?
假设Redis采用多线程设计,如下图所示,现在有两个线程A和B,线程A对一个List做LPUSH操作,并对队列长度加1。为了保证队列长度的正确性,Redis需要让线程A和B的LPUSH和LPOP串行执行,这样一来,Redis可以无误地记录它们对List长度的修改。类似的,当Redis通过recv()从一个客户端读取数据时,如果数据一直没有到达,Redis也会一直阻塞在recv()。当有多个线程要修改这个共享资源时,为了保证共享资源的正确性,就需要有额外的机制进行保证,而这个额外的机制,就会带来额外的开销。原创 2023-02-27 09:36:23 · 196 阅读 · 0 评论 -
理解redis的数据结构
这样一来,对于 List 类型的 LPOP、RPOP、LPUSH、RPUSH 这四个操作来说,它们是在列表的头尾增删元素,这就可以通过偏移量直接定位,所以它们的复杂度也只有 O(1),可以实现快速操作。2、范围操作,是指集合类型中的遍历操作,可以返回集合中的所有数据,比如 Hash 类型的 HGETALL 和 Set 类型的 SMEMBERS,或者返回一个范围内的部分数据,比如 List 类型的 LRANGE 和 ZSet 类型的 ZRANGE。而这里,我们说的数据结构,是要去看看它们的底层实现。原创 2023-02-25 16:15:00 · 438 阅读 · 0 评论 -
对redis之键值型数据库的理解
理解了数据模型,你就会明白,为什么在有些场景下,原先使用关系型数据库保存的数据,也可以用键值数据库保存。对于 Redis 而言,很有意思的一点是,它的 value 支持多种类型,当我们通过索引找到一个 key 所对应的 value 后,仍然需要从 value 的复杂结构(例如集合和列表)中进一步找到我们实际需要的数据,这个操作的效率本身就依赖于它们的实现结构。通过网络框架提供键值存储服务,一方面扩大了键值数据库的受用面,但另一方面,也给键值数据库的性能、运行模型提供了不同的设计选择,带来了一些潜在的问题。原创 2023-02-25 15:12:16 · 809 阅读 · 0 评论 -
数据库和缓存如何保证一致性--redis
Canal 模拟 MySQL 主从复制的交互协议,把自己伪装成一个 MySQL 的从节点,向 MySQL 主节点发送 dump 请求,MySQL 收到请求后,就会开始推送 Binlog 给 Canal,Canal 解析 Binlog 字节流之后,转换为便于读取的结构化数据,供下游程序订阅使用。可以看到,先删除缓存,再更新数据库,在「读 + 写」并发的时候,还是会出现缓存和数据库的数据不一致的问题。无论是「先更新数据库,再更新缓存」,还是「先更新缓存,再更新数据库」,这两个方案都存在并发问题,原创 2023-02-07 15:06:22 · 485 阅读 · 1 评论 -
缓存雪崩、击穿和穿透
通常我们为了保证缓存中的数据与数据库中的数据一致性,会给 Redis 里的数据设置过期时间,当缓存数据过期后,用户访问的数据如果不在缓存里,业务系统需要重新生成缓存,因此就会访问数据库,并将数据更新到 Redis 里,这样后续请求都可以直接命中缓存。当用户访问的数据,既不在缓存中,也不在数据库中,导致请求在访问缓存时,发现缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据,没办法构建缓存数据,来服务后续的请求。那么当有大量这样的请求到来时,数据库的压力骤增,这就是缓存穿透的问题。原创 2023-02-02 17:20:44 · 291 阅读 · 0 评论 -
主从复制和哨兵机制
offset是-1)。如果主节点坏了,这时如果要恢复服务的话,需要人工介入,选择一个「从节点」切换为「主节点」,然后让其他从节点指向新的主节点,同时还需要通知上游那些连接 Redis 主节点的客户端,将其配置中的主节点 IP 地址更新为「新主节点」的 IP 地址。对于 Redis 主节点与从节点之间的数据复制,是异步复制的,当客户端发送写请求给主节点的时候,客户端会返回 ok,接着主节点将写请求异步同步给各个从节点,但是如果此时主节点还没来得及同步给从节点时发生了断电,那么主节点内存中的数据会丢失。原创 2023-02-02 11:24:44 · 198 阅读 · 0 评论 -
Redis 过期删除策略和内存淘汰策略
传统 LRU 算法的实现是基于「链表」结构,链表中的元素按照操作顺序从前往后排列,最新操作的键会被移动到表头,当需要内存淘汰时,只需要删除链表尾部的元素即可,因为链表尾部的元素就代表最久未被使用的元素。Redis 实现的是一种近似 LRU 算法,目的是为了更好的节约内存,它的实现方式是在 Redis 的对象结构体中添加一个额外的字段,用于记录此数据的最后一次访问时间。策略的做法是,在设置 key 的过期时间时,同时创建一个定时事件,当时间到达时,由事件处理器自动执行 key 的删除操作。原创 2023-01-30 14:01:58 · 369 阅读 · 0 评论 -
Redis的持久化
AOF 日志是一个文件,随着执行的写操作命令越来越多,文件的大小会越来越大。但是,使用子进程也有一个问题需要解决,因为子进程在进行 AOF 重写期间,服务器进程还需要继续处理命令请求,而新的命令可能会对现有的数据库状态进行修改,从而使得服务器当前的数据库状态和重写后的 AOF 文件所保存的。而使用子进程,创建子进程时,父子进程是共享内存数据的,不过这个共享的内存只能以只读的方式,而当父子进程任意一方修改了该共享内存,就会发生「写时复制」,于是父子进程就有了独立的数据副本,就不用加锁来保证数据安全。原创 2023-01-29 21:05:56 · 405 阅读 · 0 评论 -
redis常用数据类型和应用场景
所以,如果消费者程序在处理消息的过程出现了故障或宕机,就会导致消息没有处理完成,那么,消费者程序再次启动后,就没法再次从 List 中读取消息了。Zset 类型(有序集合类型)相比于 Set 类型多了一个排序属性 score(分值),对于有序集合 ZSet 来说,每个存储元素相当于有两个值组成的,一个是有序结合的元素值,一个是排序值。我们都知道 Redis 提供了丰富的数据类型,常见的有五种:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)。原创 2023-01-29 19:57:31 · 553 阅读 · 0 评论 -
跳跃表
这个数据结构很陌生。跳跃表是一个有序的数据结构,每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。跳跃表支持平均logn复杂度,最坏O(n)复杂度的节点查找。还可以通过顺序操作批量处理节点。大部分情况下,跳跃表的效率可以和平衡树媲美,而且跳跃表实现较为简单,很多程序用跳跃表代替平衡树。跳跃表作为有序集合键的底层实现。如果一个有序集合包含元素数量较多或者元素的成员是比较长的字符串,就会使用跳跃表。1、跳跃表的实现...原创 2021-05-13 13:22:38 · 146 阅读 · 0 评论 -
链表和字典
1.链表链表提供了强大重排能力和顺序访问方式。列表键的底层实现就是链表。比如一个列表键包含了1到1024的整数。链表节点是一个具有值,指向前一个元素的指针,指向后一个元素的指针。所以这是一个双向链表。且链表中含有复制判断是否相等长度等函数。方便我们使用。2.字典Redis数据库底层实现就是字典。此外,哈希键的实现也是字典,...原创 2021-05-04 19:58:02 · 1018 阅读 · 0 评论 -
简单动态字符串
在Redis中,默认字符串的表示没有使用C语言中的字符数组(char * arr=‘abcd’),而是自己定义了一个简单动态字符串SDS.只有那些不需要修改的字符串才用字符数组。比如打印日志时。在数据库中,所有用到字符串的键值对都是SDS表示的比如:set msg ‘aaa’;键就是SDS表示的msg,值就是SDS表示的aaa。除此之外,SDS还被用来表示AOF模块中的缓冲区和客户端输入缓冲区。可以说SDS是Redis的基本数据结构。1、SDS的定义从这个SDS定义可以看出,他其实是一个结构原创 2021-04-02 22:18:40 · 101 阅读 · 0 评论