Skip to content

Redis基础

Redis介绍

特点

  1. 内存数据库, 速度快, 也支持数据的持久化。
  2. Redis不仅仅支持简单的key-value类型的数据, 同时还提供Lists、Hashes、Sets、Sorted Sets等多种数据结构的存储。
  3. Redis支持数据的备份(master-slave) 与集群(分片存储), 以及拥有哨兵监控机制。
  4. 支持事务。

优势

  1. 性能极高: Redis能读的速度是1100000次/秒, 写的速度是81000次/秒。

  2. 丰富的数据类型: Redis支持Lists、Hashes、Sets、Sorted Sets等数据类型操作。

  3. 原子操作: Redis的所有操作都是原子性的, 同时Redis还支持对几个操作合并后的原子性执行(事务)。

  4. 丰富的特性: Redis还支持publish/subscribe、通知、key过期等特性。

  5. 高并发原理:

    1. Redis是纯内存数据库, 所以读取速度快。
    2. Redis使用的是非阻塞IO, IO多路复用, 减少了线程切换时上下文的切换和竞争。
    3. Redis采用了单线程的模型, 保证了每个操作的原子性, 也减少了线程的上下文切换和竞争。
    4. Redis存储结构多样化, 不同的数据结构对数据存储进行了优化加快读取的速度。
    5. Redis采用自己实现的事件分离器, 效率比较高, 内部采用非阻塞的执行方式, 吞吐能力比较大。

区别

  1. Redis、Memcached、Ehcache的区别

哨兵模式

  1. 多节点条件下, master节点负责写, slave节点负责读。

基本数据类型

字符串(strings)

字符串是Redis最简单的储存类型, 他存储的值可以是字符串, 整数或者浮点数, 对整个字符串或者字符串的其中一部分执行操作;对整数或者浮点数执行自增(increment)或者自减(decrement)操作。

Redis的字符串是一个由字节组成的序列, 跟Java中的ArrayList有些类似, 采用预分配冗余空间的方式来减少内存的频繁分配, 内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时, 扩容都是加倍现有的空间, 如果超过1M, 扩容时一次只会多扩1M的空间。需要注意的是字符串最大长度为512M。

应用场景

字符串类型在工作中使用广泛, 主要用于缓存数据, 提高查询性能。比如存储登录用户信息、电商中存储商品信息、可以做计数器(想知道什么时候封锁一个IP地址(访问超过几次))等等。

指令操作

shell
set key value: 添加一条String数据类型
get key: 获取一条String类型数据
mset key1 value1 key2 value2: 添加多条String数据类型
mget key1 key2: 获取多条String类型数据
incr key: 自增(+1)
incrby key step: 按照步长(step)自增
decr key: 自减(-1)
decrby key step: 按照步长(step)递减

散列(hashes)

散列相当于Java中的HashMap, 内部是无序字典。实现原理和HashMap一致。一个哈希表由多个节点, 每个节点保存一个键值对。

与Java中的HashMap不同的是, rehash的方式不一样, 因为Java的HashMap在字典很大时, rehash是个耗时的操作, 需要一次性全部rehash。Redis未了高性能, 不能堵塞服务, 所以采用了渐进式rehash策略。

渐进式rehash会在rehash的同时, 保留新旧两个hash结构, 查询时会同时查询两个hash结构, 然后在后续的定时任务中以及hash操作指令中, 循序渐进地将旧hash的内容一点点迁移到新的hash结构中。当搬迁完成了, 就会使用新的hash结构取而代之。

当hash移除了醉后一个元素之后, 该数据结构自动被删除, 内存被回收。

应用场景

Hash也可以同于对象存储, 比如存储用户信息, 与字符串不一样的是, 字符串是需要将对象进行序列化(比如json序列化)之后才能保存, 而Hash则可以将用户对象的每个字段单独存储, 这样就能节省序列化和反序列化的时间。如下: 此外还可以保存用户的购买记录, 比如key为用户id, field为商品id, value为商品数量。同样还可以用于购物车数据的存储, 比如key为用户id, field为商品id, value为购买数量等等。

指令操作

shell
# 添加某个属性值
hset keyname field1 value1 field2 value2
# 获取某个属性值
hget keyname field
# 获取多个属性值
hmget keyname field
# 获取所有属性值
hgetall keyname
# 获取属性数量
hlen keyname
# 自增
hincrby keyname field step
# 删除(通用)
del keyname
# 删除(hash用)
hdel keyname

列表(lists)

Redis中的lists相当于Java中的LinkedList, 实现原理是一个双向链表(其底层是一个快速列表), 即可以支持反向查找和遍历, 更方便操作。插入和删除操作非常快, 时间复杂度为O(1), 但是索引定位很慢, 时间复杂度为O(n)。

应用场景

lists的应用场景非常多, 可以利用它轻松实现热销榜;可以实现工作队列(利用lists的push操作, 将任务存在lists中, 然后工作线程再用pop操作将任务取出进行执行);可以实现最新列表, 比如最新评论等。

指令操作

shell
# 左进
lpush key value1 value2 value3...
# 左出
lpop key
# 右进
rpush key value1 value2 value3...
# 右出
rpop key
# 搜索
lrange student 0 1

集合(sets)

集合类似Java中的HashSet, 内部实现是一个value永远为null的HashMap, 实际就是通过计算hash的方式类快速重排的, 这也是set能提供判断一个成员是否能在集合内的原因。

用户场景

Redis的sets类型是使用哈希表构造的, 因此复杂度是O(1), 它支持集合内的增删改查, 并且支持多个集合间的交集、并集、差集操作。可以利用这些集合操作, 解决程序开发过程当中很多数据集合间的问题。比如计算网络独立ip, 用户画像中的用户标签, 共同好友等功能。

指令操作

shell
# 添加内容
sadd key value1 value2
# 查询key里所有的值
smembers key
# 移除key里面的某个value
srem key value
# 随机移除某个value
spop key
# 返回两个set的并集
sunion key1 key2
# 返回key1踢出交集的那部分(差集)
sdiff key1 key2
# 与siffer相反, 返回交集
sinter key1 key2

有序集合(sorted sets)

sorted sets是Redis类似于SortedSet和HashMap的结合体, 一方面它是一个set, 保证了内部value的唯一性, 另一方面它可以给每个value赋予一个score, 代表这个value的排序权重。内部使用HashMap和跳越表(SkipList)来保证数据的存储和有序, HashMap里放的是成员到score的映射, 而跳越表里存放的是所有的成员, 排序依据是HashMap里存的score, 使用跳越表的结构可以获得比较高的查找效率, 并且在实现上比较简单。sorted sets中最后一个value被移除后, 数据结构自动删除, 内存被回收。

跳越列表(SkipList)

sorted sets内部的排序功能是通过「跳越列表」数据结构来实现的, 它的结构非常特殊也比较复杂。因为zset要支持随机的插入和删除, 所以它不好使用数组来表示。

这就要用到跳越列表, 在本质上跳越列表就是通过一套随机升级的方法, 实现多级链表(缓存), 再通过这样的缓存一级一级向下搜索。具体过程描述为, 当在跳越表中查找一个元素x:

  1. 从最上层的链(Ln)的开头开始。
  2. 假设当前位置为p, 它向右指向的节点为q(p与q不一定相邻), 且q的值为y。 将y与x作比较:
    1. x=y 输出查询成功
    2. x>y 从p向右移动到q的位置
    3. x<y 从p向下移动一格
  3. 如果当前位置在最底层的链中(L0), 且还要向下移动的话, 则输出查询失败

应用场景

主要应用于根据某个权重进行排序的队列的场景, 比如游戏积分排行榜, 设置优先级的任务列表, 学生成绩等。

指令操作

shell
# 添加元素
zadd key score value [score value...]
# 获取集合的值并按照score从小到大排列, 最小的是最上面
zrange key start end
# 返回有序集key中, 所有score值介于min和max之间(闭区间)的成员。有序集成员按score值递增次序排列
zrangebyscore key scorestart scoreend
# 删除
zrem key value
# 统计元素
zcard rank
# 按照score大小统计
zcount rank scorestart scoreend
# 获取位置
zrank key value
# 获取反转位置
zrevrank key value