redis

· 1617 words · 4 minute read

redis 安装使用 🔗

redis 安装
    wget https://download.redis.io/releases/redis-6.2.4.tar.gz
    tar -zvxf redis-6.2.4
    mv ~/redis-6.2.4    /usr/local/redis
    cd /usr/local/redis
    make 编译
    make PREFIX=/usr/local/redis  install 安装
        加prefix指定可执行文件的目录。
        不加的话可执行文件会放到/usr/local/bin目录中,
        库文件在/usr/local/lib中。
redis 使用
    在/usr/local/redis目录下,
        /bin/redis-server& ./redis.conf
        or /bin/redis-server ./redis.conf     
        redis-cli      -p 6379

redis 高可用 🔗


支持的数据结构 🔗

  支持的value类型: 字符串,hash,列表,集合,有序集合

  • redis未直接使用上述数据结构, 而是redisObject结构进行封装。
    • redisObject结构体: type, encoding, prt, refcount, lru...
    • 键对象, 均为字符串对象
    • 值对象
      • 1字符串对象
      • 2列表对象(ziplist, linkedlist)
      • 3哈希对象(ziplist, hashtable)
      • 4集合对象(intset, hashtable)
      • 5有序集合对象(ziplist, skiplist)

hash表的实现方式 🔗

  hash表的存储结构: array[ListNode; n]
  扩容方式:设计到内存迁移,但不能阻塞线程太长时间。采用了 渐进式 rehash + 定时 rehash
  渐进式rehash:在每次crud时判断hash表如果处于rehash的状态, 则rehash当前的一个节点。
  定时rehash:添加时间事件进行迁移。

有序集合的实现方式 🔗

  待排集合的大小影响数据结构的选择。
当集合的大小超过可配阈值时,选择skiplist: 一种多层级的链表, 节点有多个指向后序节点的指针。

事件(IO事件 + 时间事件) 🔗

  io事件是redis正常的业务请求处理。 时间事件是redis内部为了维持正常状态的需要。

事件驱动框架 🔗

void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    while (!eventLoop->stop) {
        aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
    }
}
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
    if (eventLoop->beforesleep != NULL && flags & AE_CALL_BEFORE_SLEEP)
            eventLoop->beforesleep(eventLoop);   
    
    numevents = aeApiPoll(eventLoop, tvp);
 
    /* After sleep callback. */
    if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP)
            eventLoop->aftersleep(eventLoop);
     /* 处理io事件 */
     for (j = 0; j < numevents; j++) {...}

     /* 处理时间事件(del key, rehash..) */
    if (flags & AE_TIME_EVENTS)
        processed += processTimeEvents(eventLoop);
    
    return processed; /* return the number of processed file/time events */
}

时间事件 (定时事件, 周期性事件) 🔗

  • 更新db的统计信息
  • 清理过期key, rehash
  • 关闭和清理无效的客户端
  • 尝试AOF RDB
  • 主从同步

io多路复用 🔗

  epoll 实例内部维护了两个结构。
分别是记录要监听的文件描述符(rb_tree)和已经就绪的文件描述符(ready_queue)。不用select, poll去遍历文件描述符集合查看是否ready。
  Reactor模式

过期键删除 🔗

  1. 定时删除, 创建定时器添加到时间事件的链表中
  2. 懒性删除策略(db.c/expireIfNeeded) get key -> expireIfNeeded -> 删除key 并返回nil
  3. 定期删除策略, 创建时间事件 (redis.c/activeExpireCycle)
  4. save 生成rdb文件, 不会保存过期键

持久化 🔗

  RDB:

  • save (阻塞redis服务进程,直到RDB文件生成完毕)
  • bgsave 创建子进程来创建全局快照RDB文件

  AOF持久化:

  • 配置appendfsync
    • always 每次写都fsync
    • everysec 每秒fsync
    • no 由os定

  AOF 重写(减少AOF文件的大小), bgrewriteaof创建子进程去执行。 redis中SIGUSR1信号关闭子进程的aof,rdb子进程。

  AOF rewrite重写的过程:

  • 创建新AOF文件,通过读当前db的键的value用最新的kv记录到新AOF文件中。
  • 子进程在AOF重写过程中, 发生的write操作,主服务进程会写到AOF重写缓冲区,等子进程结束后,父进程收到信号,将AOF重写缓冲区的内容append到AOF文件中,完成AOF文件的后台重写。

事务与lua 🔗

  redis单线程处理事件,所以事务执行是串行的,所以满足隔离性。
  multi, exec, watch。 multi之后 的命令入队列, exec执行队列命令。

  • multi之后 redisclient的flags事务标示打开, 请求的命令入队列(入队失败,不会执行事务)
  • exec执行队列命令(执行失败,事务继续执行)。
  • watch的key被修改后,redisClient的REDIS_DIRTY_CAS会打开, exec时会取消执行

redis实现令牌桶限流 🔗

  在redis中维护List对戏那个,作为令牌桶。
  生产者每隔interval秒放令牌桶中放r个令牌, lpush。   消费者brpop从list中获取令牌。

redis 和 mysql 如何保证数据一致性 🔗

  在满足实时性的条件下,只有最终一致性方案。