Redis单机基础知识

(1). Redis底层IO多路复用 

PS:首先说下,Redis启动的一个进程,并不是只有一个线程,只是处理IO,验证命令和执行命令都是单个线程执行。还有其他的模块,比如内存淘汰,处理一些异常数据还是多线程的。

1.select存在的问题:

代码: 用bitmap来表示哪个fd有数据,然后检测到有数据就标识1然后通过内核态和用户态的切换提高检测效率.去取出数据的时候,会根据max_id来遍历.

==>但是上面这个逻辑,并不是死循环执行的,当有socket buffer有数据的时候,会有中断程序来让其执行上面的逻辑。相当于是一个异步通知的功能,但是这种逻辑遍历是O(n).

 

缺点:

1.bitmap有上限 1024,代表监听的FD有上限

2.,每次检测的时候都要重新创建bitmap.

3.用户内核切换开销

4.select的时候又要扫描,用户和kernal都需要反复遍历所有FD.

 

2.poll解决的问题

1.其实存在的问题和select一样,唯一解决的是监听的socket fd上限。

 

3.epoll解决的问题

1.和poll一样,fd-event拼成一个对象放入list中,解决了连接上限问题

2.和poll一样,event标记POLLIN代表有新数据

3.省去了内核态和用户态copy数据切换的过程,共享资源. (共享内存,mmap什么的)

4.由于epoll_wait的时候会将置位以重排的形式进行,那么前nfds(返回值)个数据就是有新数据的.

可以将O(n)->O(1).极大提高效率.  ==>其实底层用了红黑树管理链接的socket以及数据来了返回的数据用链表挂载返回给上层.

 

(2). Redis基础的数据结构

1.String类型

2.Map类型

3.Set类型

4.Sorted Set类型  

5.双端list类型

讲下Sorted Set类型,底层使用跳表结构:

简单描述以下:每个节点就是多级链表结构,查找和插入的时间复杂度都是log(n),1/2的概率来增加级数。每次从顶至下查找。

曾经有个热点问题:为什么Sorted Set不用红黑树结构?==>那么现在我们对比下两个数据结构

1.相同节点个数的情况下,由于跳表通过概率计算来确定层级,各个节点指针内存的密集层度比较低,比较松散,不像红黑树每个节点都会有两个指针。

2.红黑树在测试的情况下,其实常数项下查询效率是比跳表快的,虽然大家都是log(n)

3.在并发高的插入删除情况下,红黑树调整的常数项比跳表大,红黑树相当于全局的调整,而跳表只需要前后节点的调整。

4.跳表的代码实现比红黑树简单

==>综上所述,由于Redis 单机QPS 2w著称,为了保持调整时的快以及稳定性,选择了跳表,且作者也曾说过,不会为了优化Sorted Set而复杂化代码以及失去并发情况下的稳定性。

原话如下:

There are a few reasons:

  1. They are not very memory intensive. It's up to you basically. Changing parameters about the probability of a node to have a given number of levels will make then less memory intensive than btrees.
  2. A sorted set is often target of many ZRANGE or ZREVRANGE operations, that is, traversing the skip list as a linked list. With this operation the cache locality of skip lists is at least as good as with other kind of balanced trees.
  3. They are simpler to implement, debug, and so forth. For instance thanks to the skip list simplicity I received a patch (already in Redis master) with augmented skip lists implementing ZRANK in O(log(N)). It required little changes to the code.About the Append Only durability & speed, I don't think it is a good idea to optimize Redis at cost of more code and more complexity for a use case that IMHO should be rare for the Redis target (fsync() at every command). Almost no one is using this feature even with ACID SQL databases, as the performance hint is big anyway.
    About threads: our experience shows that Redis is mostly I/O bound. I'm using threads to serve things from Virtual Memory. The long term solution to exploit all the cores, assuming your link is so fast that you can saturate a single core, is running multiple instances of Redis (no locks, almost fully scalable linearly with number of cores), and using the "Redis Cluster" solution that I plan to develop in the future.

 

 

(3)Redis 内存淘汰策略

1.主动淘汰

模拟场景,当用户写入k-v时,发现内存超过配置的maxmemory的时候,触发主动淘汰策略。淘汰策略有以下6种:

1.1. volatile-lru   设置了过期时间的key会根据LRU淘汰

1.2. volatile-ttl    设置了过期时间的key中根据其TTL,越大越优先淘汰(好奇为什么不是小的先淘汰。。)

1.3. volatile-random    设置了过期时间的key随机挑选淘汰

1.4  allkeys-lru     全体的key根据LRU淘汰

1.5  allkeys-random     全体的key随机淘汰

1.6  no-enviction     新写入数据会报出异常,且不会执行    

 

2.被动淘汰

设置了过期时间的key,如果过期了,不一定会被定时检测的线程检测到然后淘汰。

==>等到下次访问请求到此key时,发现已经存在此key,但key已经过期,那么再删除此key,返回结果。

 

3.拓展,写段LRU代码(put,get方法都是O(1)) TODO

 

(4)Redis 渐进式rehash

前面有提到过Redis的Map类型(其实就是字典),底层也是通过哈希表来实现,我们知道当Java中HashMap中由于Hash冲突会挂链表的形式来解决,等到所有节点的数量某个阈值时,出于链表性能的考虑,会进行Resize的操作。Redis也一样。

==>

HashMap中是直接在一个方法操作中,生成一个二倍大的数组,然后做全量的迁移。Redis则对这种策略进行了优化,为了避免rehash对服务器性能进行影响,它将rehash的操作均摊到了对字典的每个add,delete,update,get操作上。

==>

其实简言之,就是生成了一个二倍大的数组,但是并没有一下子将所有的节点都迁移过去,如果你要查找一个key,依旧在old table上找,old table上不存在,则去new table上找。另外在渐进式执行期间,如果old table上的key存在,则将其迁移到new table上,然后返回。 ==>在这种过程下,old table的数量只减不增,最终完成不阻塞的rehash。

==>

当然渐进式rehash也同样带来了问题:


渐进式rehash避免了redis阻塞,可以说非常完美,但是由于在rehash时,需要分配一个新的hash表,在rehash期间,同时有两个hash表在使用,会使得redis内存使用量瞬间突增,在Redis 满容状态下由于Rehash会导致大量Key驱逐。

 

(5)Redis事务机制

有些时候,某些应用场景需要利用到Redis的事务机制。那么来讲解下multi/exec这两个操作命令的原理机制。

1. 当某客户端,发起multi指令时,该客户端之后所有的操作都进行到一个队列中,调用exec指令到时候,才会按顺序执行这些所有的指令。

2. 可是是如何做到错误回滚的呢?其实没有实现Mysql那种回滚操作,它会检查所有的指令的正确性,如果错误就不会执行,否则就执行所有指令。

PS: Redis开发者表示,如果出现一半成功,一半失败的结果,那就是你业务代码没有考虑全面导致。

3. 另外,一般在执行事务时,需要锁定某些key,让其他客户端不能在这时期修改这些key,可以用watch指令。

所有事务相关指令汇总:

       watch key1 key2 ... : 监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 )

  multi : 标记一个事务块的开始( queued )

  exec : 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 ) 

  discard : 取消事务,放弃事务块中的所有命令

  unwatch : 取消watch对所有key的监控

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值