redis 底层原理实现 - Go语言中文社区

redis 底层原理实现


1.Redis 数据类型 Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

2.简单动态字符串

c字符串是以空字符结尾的字符串,redis中使用sds(Simple Dynamic String, 简单动态字符串)代替c字符串,sds定义:
buf 保存了字符串信息   free为预留出的空间 len为长度
区别如下:
在这里插入图片描述

  • set(单次设置)
  • get(单次读取)
  • mset(批量设置)
  • mget(批量读取)
  • exists(是否存在)
  • del(删除)
  • expire key time(设置过期时间e)
  • setex (== set + expire)
  • incr key number ( incr age -5 )
  • setbit
  • getbit

位图

会有一些 bool 型数据需要存取,比如用户一年的签到记录, 签了是 1,没签是 0,要记录 365 天。如果使用普通的 key/value,每个用户要记录 365 个,当用户上亿的时候,需要的存储空间是惊人的。
位图不是特殊的数据结构,它的内容其实就是普通的字符串,也就是 byte 数组。

3.字典

Redis数据库使用字典来作为底层实现的。对数据库的增删改查也是构建在对字典的操作之上
字典的底层是使用哈希表来实现的。
保存k0, v0
在这里插入图片描述

  • hset
  • hget
  • hlen

4.list

首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是 ziplist,也即是 压缩列表。它将所有的元素紧挨着一起存储,分配的是一块连续的内存。当数据量比较多的 时候才会改成 quicklist。因为普通的链表需要的附加指针空间太大,会比较浪费空间,而且 会加重内存的碎片化。比如这个列表里存的只是 int 类型的数据,结构上还需要两个额外的指针 prev 和 next 。所以 Redis 将链表和 ziplist 结合起来组成了 quicklist。也就是将多个 ziplist 使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空 间冗余。

  • rpush
  • rpop
  • lpush
  • lpop
  • lrange key start stop
  • lindex key index

5.集合

  • sadd
  • smembers key (获取所有元素)
  • sismember key value (查找某个value是否存在)

6.zset 有序集合

  • zadd
  • zrange keys

7.分布式锁

2.8以前不是原子的。(有可能造成死锁)

  • setnx (set if not exists)
  • expire (设置过期时间)
  • del
    2.8以后(原子操作)
  • set name liu ex 5 nx

8.延时队列

  • brpop/blpop (阻塞)
  • 但是要注意空闲链接的问题。 长时间闲置,服务器会主动断开连接。会抛出异常

9 HyperLoglog

提供不精确的计数方案,标准误差是0.81%

  • pfadd
  • pfcount
  • pfmerge (多个pf merge)

10 布隆过滤器(4.0)

布隆过滤器可以理解为一个不怎么精确的 set 结构,当你使用它的 contains 方法判断某 个对象是否存在时,它可能会误判。但是布隆过滤器也不是特别不精确,只要参数设置的合 理,它的精确度可以控制的相对足够精确,只会有小小的误判概率。
当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存 在。打个比方,当它说不认识你时,肯定就不认识;当它说见过你时,可能根本就没见过 面,不过因为你的脸跟它认识的人中某脸比较相似 (某些熟脸的系数组合),所以误判以前见 过你。

  • bf.add
  • bf.exists

11.事务

  • multi
  • incr books
  • exec(执行)/discard(放弃)

Redis 的事务根本不能算「原子性」,而仅仅是满足了事务的「隔 离性」,隔离性中的串行化——当前执行的事务有着不被其它事务打断的权利。

上面的 Redis 事务在发送每个指令到事务缓存队列时都要经过一次网络读写,当一个事 务内部的指令较多时,需要的网络 IO 时间也会线性增长。所以通常 Redis 的客户端在执行 事务时都会结合 pipeline 一起使用,这样可以将多次 IO 操作压缩为单次 IO 操作

优化:使用管道

  • pipe = redis.pipeline(transaction=true)
  • pipe.multi()
  • pipe.incr(“books”)
  • pipe.incr(“books”)
  • values = pipe.execute()

12 过期策略

1.定是删除

  • 把redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,以后会定时遍历这个 字典来删除到期的 key。除了定时遍历之外,它还会使用惰性策略来删除过期的 key,所谓 惰性策略就是在客户端访问这个 key 的时候,redis 对 key 的过期时间进行检查,如果过期 了就立即删除。定时删除是集中处理,惰性删除是零散处理。

2.惰性删除

https://baijiahao.baidu.com/s?id=1594341157941741587&wfr=spider&for=pc

13 持久化

  1. rdb (快照)

redis是单线程的,在保存快照的时候,会调用操作系统的cow(copy on write), fork 出一个子进程,主进程继续处理请求,子进程进行copy。当主进程有修改时,会复制一个大小为4k的分页,这个操作对子进程来说是透明的。所以子进程复制的还是修改的之前的内容。这样就会导致有数据丢失

  1. aof

aof保存的是指令流,当redis接受一个指令时,会先保存指令,在执行。这样根据aof可以把一个空的redis 恢复成原先的样子。但是随着时间的增长,aof会变得越来越大。因为redis是单线程的,所以恢复的时候会特别缓慢,可能无法处理其他的请求。

  1. 混合型持久(4.0)

在redis4.0版本,退出了混合持久性。在rdb开启的时候,进行aof操作,这样的保存快照的时候操作也可以被保存下来,保证数据不会丢失,同时也解决了aof臃肿的问题。

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_43019193/article/details/89968319
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢