• 2----------原理篇


    Redis事单个线程程序

    Nginx也是单线程,但是他们都是服务器高性能的典范

    redis为什么这么快?

    所有数据都在内存中,所有的运算都是内存级别的运算

    Redis既然是单线程,如何能处理那么多的并发客户连接?

    多路复用

    非阻塞IO

    当我们调用套接字(socket网络编程)默认是阻塞的;

    阻塞:非要读到数据才返回,非要写完才返回

    非阻塞IO:在套接字对象上提供一个选项Non_Blocking,当这个选项打开时,读写方法不会阻塞,而是能读多少度多少,能写多少写多少。

    1. 能读多少:取决于内核为套接字分配的读缓冲区内部的数据字节数
    2. 能写多少:取决于内核为套接字分配写缓冲区的空闲空间字节数
    3. 返回值高数程序实际读写了多少字节

    事件轮询(多路复用)

    简单的轮询API是select函数,操作系统提供给用户程序的API。来一个处理一个,来了事件就立即返回,死循环。-----不是很懂,大概跟底层相关吧。

    指令队列

    Redis会将每个客户端套接字都关联一个指令队列。客户端的指令通过队列来排队进行顺序处理,先到先服务。

    相应队列

    Redis为每一个客户端套接字关联一个响应队列。rRedis服务器通过响应队列来讲指令的返回结果回复给客户端。

    定时任务

    假如线程阻塞在select系统调用上,定时任务无法准时调度怎么办

    定时任务记录在一个最小堆的数据结构中。快执行的任务放在堆的最上方。

    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    通信协议

    RESP

    Redis序列化协议------文本协议

    就是将客户端发送给服务器的指令按照一定的规则转换为文档;服务器将返回结果经过编码发送回来。说白了就是序列化协议。

    持久化

    Redis的数据全部在内存里,突然宕机,数据就会全部丢失,因此需要一种机制来保证Redis的数据不会因为故障而丢失,这个叫作Redis的持久化。

    持久化方式:

    1. 快照
    2. AOF日志

    快照原理

    原理:将内存数据的二进制序列化,然后复制一份到文件中

    简单的:fork一个子进程,将此时的内存数据复制一份在内存中,然后进行快照。

    那么产生的问题就是:在复制完成的过程中,内存发生的数据改变是无法感知的。跟CopyOnWriteList一摸一样

    AOF日志

    记录指令,恢复Redis时需要进行重放这些指令,很慢。

    Redis每次执行指令结束之后,执行完的指令再放入日志存盘。

    所以:这个日志会越来越长,而且恢复会很耗时,导致Redis长时间无法对外提供服务,所以需要AOF定期瘦身

    ------Redis提供了bgrewriteaof指令用来给AOF文件日志瘦身

    fsync

    保证机器宕机的时候,也保证指令的不丢失

    运维

    通常Redis的主节点不会进行持久化操作,持久化操作主要在从节点进行,从节点是备份节点,没有来自客户端请求的压力,它的操作系统资源往往比较充沛。

    混合持久化

    前半段用快照,后半段用最新的AOF文件

    管道

    就是将连续的几个任务请求一起发送过去,然后结果一起发送过来。是客户端提供的功能,其实没东西的

    事务

    Redis对应的事务指令为:multi、exec、discard。

    multi
    
    incr books
    
    incr books
    
    exec

    所有指令在exec之前不执行,而是缓存在服务器的一个事务队列中,服务器一旦收到exec指令,才开始执行整个事务队列,执行完毕后一次性返回所有指令的运行结果。

    注意:假如事务的中间有一条指令出错,那么后续的还是能执行的。所以说redis并不能保证原子性。

    discard 丢弃

    跟exec是对立的

    优化

    Redis事务在发送每个指令到事务缓存队列时都要经历一次网络读写,当一个事务内部的指令较多时,需要的网络IO就会变长,所以通常Redis的客户端在执行事务时都会结合管道一起使用。

    watch

    乐观锁

    watch books
    
    incr books
    
    multi
    
    incr books
    
    incr books
    
    exec

    上述代码执行失败,因为books在事务之前改变了。

    PubSub消息队列

    上一篇讲了Redis消息队列的使用方法,但是没有提到Redis消息队列的不足,不保证准确性和多播机制

    消息多播允许生产者只生产一次信息,由中间件负责将信息复制到多个消息队列,每个消息队列由相应的消费组进行消费。

    为了解决多播问题:Redis提供了PubSub(PublisherSubscriber)发布者/订阅者模式。

    消费者

    订阅了name这个主题

    import redis
    import time
    
    client = redis.StrictRedis()
    p = client.pubsub()
    p.subscribe("name")
    while true:
        msg = p.get_message()
        if  not msg:
            time.sleep(1)
            continue;
        print msg

    生产者

    import redis
    
    client = redis.StrictRedis()
    client.publish("name", "java")
    client.publish("name", "python")
    client.publish("name", "go")

    所有的消费者都会收到相同的信息

    但是休眠也不是个事,用监听来代替休眠

    import redis
    import time
    
    client = redis.StrictRedis()
    p = client.pubsub()
    p.subscribe("name")
    for msg in p.listen():
        print msg

    当信息来了,就知道了

    模式订阅

    一次订阅多个主题

    缺点

    无法确认消费者是否收到

    并且不会持久化

    小对象的压缩

    Redis是一个非常耗费内存的数据库,它的所有数据都放在内存里

    zipList

    内部管理的集合数据结构很小,他会使用紧凑存储形式压缩存储

    本来时Hashmap存储的,改用一维数组存储下。

    如果hash结构,那么key和value会作为两个entry被相邻存储

    object encoding key:查看是用什么格式存储的

    intset

    当set元素少的时候且都是整数的时候,紧凑的整数数组存储

    总结:小对象超过一定的数量之后就必须使用标准的存储结构

    内存回收机制

    页回收,只要页上还有一个key,就不会回收,但是会重新使用那些尚未回收的空闲内存

    内存分配算法

    内存分配是一个非常复杂的课题,需要适当的算法划分内存页,需要考虑内存碎片,需要平衡性能和效率。

    redis不管内存的,但是有第三方库管理:

    1. jemalloc(facebook)
    2. tcmalloc(google)

    jemalloc的性能好一些,所以redis默认使用了jemalloc

  • 相关阅读:
    【codevs4919】线段树练习4
    【51Nod1405】树上距离和 二次扫描与换根法
    【洛谷P1717】钓鱼
    【洛谷P1230】智力大冲浪
    【洛谷P1248】加工生产调度
    hdu 1195(搜索)
    hdu 1181(Floyed)
    hdu 1250(大整数)
    hdu 1180(广搜好题)
    hdu 1099(数学)
  • 原文地址:https://www.cnblogs.com/sicheng-li/p/13298086.html
Copyright © 2020-2023  润新知