Fork me on GitHub

Redis学习

了解Redis

Redis介绍

REmote Dictionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。

Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

Redis特点

  • Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
  • Redis支持数据的备份,即master-slave模式的数据备份。

Redis支持的数据结构

  • 简单动态字符串 (simple dynamic string SDS)
    • 空间预分配:若SDS长度大于1MB,程序会分配1MB的未使用空间。
    • 惰性空间释放
    • 二进制安全
    • 获取字符串长度为常数复杂度
    • 杜绝缓冲区溢出
  • 链表
  • 字典
  • 跳跃表
  • 整数集合(intset)
    • 不包含重复项
    • 从小到大有序排列
  • 压缩列表(ziplist)
    • 压缩列表是为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序性数据结构
    • 压缩链表可以包含多个节点,每个节点可以保存一个字节数组或整数值。

Redis对象类型

  • 字符串对象 string
  • 列表对象 list
    • 当列表元素较少时,使用压缩列表作为底层实现(所有字符串长度都小于64KB,且元素数量小于512)
    • 当列表元素较多时,使用双端链表作为底层实现
  • 哈希对象 hash
    • 当所有键值对的键和值的字符串长度都小于64KB,且键值对数量小于512时,哈希对象采用压缩列表作为底层实现,每对键值保存在相邻位置。
    • 否则采用hashtable编码,即采用字典作为底层实现,字典的每个键值都是字符串对象。
  • 集合对象 set
    • 当所有元素都是整数值且个数不超过512时,采用intset作为底层实现。
    • 否则采用hashtable编码,即字典作为底层实现,字典的键对应集合中的元素,字典的值全部为NULL
  • 有序集合对象 zset
    • 当所有元素的长度小于64KB且数量小于128个时,采用ziplist作为底层实现
    • 否则采用zset作为底层实现,zset结构同时包含一个字典和一个跳跃表,这两种数据结构通过指针共享相同的元素成员和分值。

Redis的内存回收机制

  • 引用计数机制:

    • 创建一个新对象时,引用计数的值被初始化为1
    • 对象被一个新程序使用时,引用计数值加1
    • 对象不再被一个程序使用时,引用计数值减1
    • 对象的引用计数值为0时,对象所占用的内存会被释放

      Redis的对象共享机制

  • Redis中,多个键可以共享同一个值对象:

    • 将数据库的键指针指向一个现有的值对象
    • 并将被共享的值对象的引用计数加1
  • redis在初始化服务器时会创建一万个字符串对象,包含了从0-9999的所有整数值,当服务器要用到这些字符串对象时直接引用就可以了。

Redis的复制

即主从服务器的同步。

  • 旧版复制功能:
    • SYNC命令:
      • 主服务器执行BGSAVE命令生成RDB文件,此过程消耗大量的CPU、内存和磁盘I/O资源
      • 发送RDB文件给从服务器,消耗网络资源
      • 从服务器载入RDB文件时会因为阻塞而无法处理命令请求
  • 新版复制功能:
    • 完整重同步:
      • 初次复制时使用完整重同步,操作与SYNC命令相同。
    • 部分重同步:
      • 用于断线后重新复制的情况

Redis源码探究

Redis服务器

  1. server.h: redisServer结构体中含有服务器全局信息的相关属性
  2. server.c/main: 程序创建一个redisServer结构的实例变量 server , 调用函数 initServerConfig() , 将 server 的各个属性初始化为默认值。
  3. server.c/main: 读入配置文件及参数设置等,打印log
  4. server.c/main: 配置完毕后启动aeMain,进入时间循环
  5. ae.c/aeMain: 循环执行aeProcessEvents直到接收到stop信号
  6. ae.c/aeProcessEvents: 调用multiplexing API – aeApiPoll,只有当超时或某些事件被触发时才会返回。
  7. ae_epoll.c/aeApiPoll: 调用epoll_wait接收请求,返回事件数量
  8. ae.c/aeProcessEvents: 根据返回的事件数量处理队列中的事件。一般情况下,首先处理读事件,再处理写事件。

由于采用了I/O多路复用技术,即使有多个连接存在,Redis服务器在处理网络请求时也是单进程单线程的。

  • 优点:
    • 代码更清晰,处理逻辑更简单
    • 不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
    • 不存在多进程或者多线程导致的切换而消耗CPU
  • 缺点:
    • 无法发挥多核CPU性能,不过可以通过在单机开多个Redis实例来完善;
    • (主从服务器如果部署在同一节点上,是否就失去了实用意义?)