Redis Basic

Redis的定位于特性

1. SQL与NoSQL
    1. 关系型数据库特点:
      1. 以表格的形式,基于行存储数据,是一个二维的模式
      2. 存储的是结构化的数据,数据存储有固定的模式,数据需要适应表结构
      3. 表与表之间存在关联
      4. 大部分关系型数据库都支持SQL(结构化查询语言)的操作,支持复杂的关联查询
      5. 通过事务(ACID酸)来提供严格或实时的数据一致性
    1. 关系型数据库的一些限制
      1. 实现扩容的话,只能垂直扩容,比如磁盘限制了数据存储,就扩大磁盘容量通过推硬件的方式,不支持动态扩容。水平扩容需要复杂的技术实现,比如分库分表
      2. 表结构修改困难,存储的数据格式也会收到限制
      3. 关系型数据库通常会把数据持久化到磁盘,在高并发和搞数据量的情况下,磁盘的读写压力会比较大
    1. 非关系型数据库的特点:
      1. 我们一般把它叫做“non-relational”或者“Not Only SQL”,NoSQL不提供SQL(Structure Query Language 结构化查询语言)
      2. 特点:
        1. 存储非结构化的数据,比如文本、图片、音频、视频;
        2. 表与表之间没有关联,可扩展性强;
        3. 保证数据的最终一致性,遵循BASE理论;Basically Available(基本可用);Soft-state(软状态);Eventually Consistent(最终一致性)
        4. 支持海量数据的存储和高并发的高效读写
        5. 支持分布式,能够对数据进行分片存储,扩缩容简单
      1. 常见的非关系型数据库
        1. KV存储:Redis和MemCached
        2. 文档存储:MongoDB
        3. 列存储:HBase
        4. 图存储:Neo4j
        5. 对象存储:
      1. NewSQL:结合了SQL和NoSQL的特性,例如TiDB(PingCAP)、ScaleDB
    1. Memcached和Redis的主要区别是什么
      1. Memcached只存储KV、没有持久化机制、不支持主从复制、是多线程的

Redis基本数据类型

Redis默认有16个库(0-15)可在配置文件中修改,redis的存储我们叫做key-value存储,或者叫字典结构。key的最大长度限制是512M

1. String 类型
    1. 可以用来存储int(整数)、float(单精度浮点数)、String(字符串)
    2. 数据模型
      1. redis 最外层是一个redisDb结构体,里面放的是dict
      2. 在redis中每个键值对都是一个dictEntry,其中,key存储的是关键字定义,val存储是value定义,next指向下一个键值对
      3. 以set hello world为例
        1. 因为key是字符串,redis自己实现了一个字符串类型,叫做SDS,所以hello 指向SDS的结构。
        2. value是world,也是一个字符串,但是没有直接指向SDS,而是存储在redisObject中。5种常见的数据类型的value都是通过redisObject存储,最终redisObject再通过指针指向一个实际的数据结构
        3. redis中string类型实际对外都是string且用的string命令,但是内部却又3中不同的编码:
          1. int,存储8个字节的长整型(long,2^64-1)
          2. embstr,代表embstr格式的SDS,存储小于44个字节的字符串
          3. raw,存储大于44个字节的字符串
        1. 常见问题:
          1. SDS是什么?
            1. 是redis中字符串的实现,Simple Dynamic String,简单动态字符串。本质上还是字符串数组,但是多了一些长度等等定义。
          1. 为什么redis要用SDS实现字符串?
          1. embstr和raw编码的区别?为什么要为不同的大小设计不同的编码?
            1. embstr的使用只分配一次内存空间(因为redisObject和SDS是连续的),而raw需要分配两次内存空间(分别为redisObject和SDS分配空间)。
            2. 与raw相比,embstr的好处是在创建时少分配一次空间,删除时少分配一次空间。而且数据是连在一起方便寻找。
            3. embstr的坏处,字符串的长度增加时需要重新分配内存,redisObject和SDS都需要重新分配,因此redis中embstr实现为只读,这种编码的内容是不能修改的
          1. int和embstr什么时候转为raw?
            1. int数据不再是整数—–raw
            2. int大小超过了long的范围(2^63-1)—–embstr
            3. embstr字节超过了44字节—–raw
          1. 明明没有超过44字节,为什么变成了raw?
            1. 前面说过,对于embstr,由于实现只是可读的,因此在对embstr对像进行修改时都会先转化为raw再进行修改。因此,只要是修改embstr对象,随后都会变成raw,无论是否到达了44个字节
          1. 当长度小于阈值时,会还原吗?
            1. 不会,编码转换在redis写入数据时完成,且转换过程不可逆,只能从小内存编码到大内存编码转换
          1. 为什么要对底层的数据结构使用redisObject进行一层封装呢?
            1. 无论是设计redisObject还是设计这么多的SDS,都是为了在存储不同的内容选择不同的存储方式,这样可以尽可能的节省内存空间和提升查询速度
2. Hash 类型
    1. Hash用来存储多个无序的键值对。最大的存储数量2^32(大概40亿左右);注意hash的value只能存储字符串,不能嵌套其他类型;field不能单独设置过期时间;
    2. 数据模型
      1. Hash类型的底层使用两种数据结构实现:ziplist 和 hashtable
        1. ziplist是一个经过特殊编码的,由连续的内存块组成的双向链表。但是他不存储上一个节点和下一个节点的指针。而是存储上一个节点的长度和当前节点的长度。这样读写可能会慢一点,但是节省内存。
          1. 什么时候使用ziplist存储当hash对象同时满足以下条件时,使用ziplist编码:
            1. hash对象保存的键值对数量<512个
            2. 所有键值对的键和值的字符串长度都<64字节(一个英文字母一个字节)
        1. hashtable数据结构
          1. 如果超过上面的阈值的任何一个,存储结构就会转化为hashtable
          2. hashtable的本质就是一个KV的结构,是一个数组 + 链表的结构!!!
          3. 前面我们知道redis的KV结构是都是通过dictEntry来实现的。但是,在hashtable中,又对dictEntry进行了多层封装。从最高层到最底层:dict->dictht->dictEntry
          4. 整体数据结构就是数组+链表。
            1. 为什么要定义两个哈希表(dictht[2]),其中一个不用呢?hash默认使用ht[0],ht[1]不会分配空间和初始化。ht[1]用来扩容迁移
            2. 什么时候触发扩容?
              1. 具体和HashMap一样,由扩容因子控制
3. List 集合类型
    1. 存储有序的字符串(从左到右),元素可以重复,最大的存储数量2^32-1(40亿左右)
    2. 数据模型:
      1. 3.2版本后统一用quicklist来存储。quicklist存储了一个双向链表,每个节点都是一个ziplist。
4. Set 集合类型
    1. Set存储String类型的无序集合,最大存储数量2^32-1(40亿左右)
    2. 数据模型:
      1. Redis使用intset或者hashtable存储set
        1. 如果数据都是整数类型就用intset类型,否则用hashtable(数组+链表);如果元素超过512个也会用hashtable存储
5. Zset 有序集合
    1. Zset存储有序的元素,每个元素都有个score,按照score从小到大排名。score相同时,按照key的ASCLL码排序
    2. 数据结构对比。
    3. 默认使用ziplist编码,在ziplist内部,按照score排序递增来存储。插入的时候要移动之后的数据。
    4. 如果元素数量大于等于128个,或者任一member长度大于等于64字节,则使用skiplist + dict存储
    5. 什么事skiplist(跳表)?
      1. 使用有序链表的问题?我们查找某个元素时,需要从头到尾逐个比较,时间复杂度O(n),当我们要插入时,同样要经历同样的过程。因此,skiplist可以解决上述问题。
      2. 查找过程:
        1. 当我们查找数据时,可以先沿着新链表查找。当碰到比待查找数据大的节点,再到下层查找。
        2. 总结,这个查找过程中,由于新增指针我们不需要与链表每个节点逐个比较了,需要比较的节点大概只有原来的一半,这就是跳表。为什么不用AVL树或者红黑树?因为跳表更简洁。
  1. 总结:

    1. img