读书笔记之Redis开发与运维-一

47次阅读

共计 4915 个字符,预计需要花费 13 分钟才能阅读完成。

前言

    读书笔记系列主要记录自己看的书籍中的知识点,算是一个归纳整理吧。Redis 在我们的日常开发中可以说是很常用了,《Redis 开发与运维》
这本书讲解了 Redis 开发和运维的方方面面,很系统、全面,关键是实用。特来撸撸它,记录一番。全书分为 14 章,下面将记录个人认为每章中重要的知识点。

一、Redis 初识

    Redis 是一种基于键值对(key-value)的 NoSQL 数据库,Redis 中的值可以是由 string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)、Bitmaps(位图)、HyperLogLog、GEO(地理信息定位)等多种数据结构和算法构成,可以满足很多的应用场景。因为 Redis 会将所有数据都放在内存里,所以它的读写性能非常好。Redis 还可以将内存的数据利用快照和日志的形式保存到硬盘上,这样发生断电或者机器故障,内存中的数据就不会丢失。当然 Redis 还提供了其他很多附加功能。

1、Redis 特性

(1)速度快

Redis 的所有数据都是放在内存里的,这是速度快的最主要原因;
Redis 是用 C 语言实现的,“距离”操作系统更近,执行速度相对会更快;
Redis 采用单线程架构,预防了多线程可能产生的竞争问题;

(2)基于键值对的数据结构

Redis 中的值不仅可以是字符串,还可以是具体的数据结构, 方面在不同应用场景的开发。Redis 主要提供五种数据结构:字符串、哈希、列表、集合、有序列表,并且在字符串的基础上演变出来了位图 (Bitmaps) 和 HyperLogLog 俩种“数据结构”。Redis3.2 版本加入了 GEO(地理信息定位)的功能。

(3)丰富的功能

除了 5 种数据结构,还有其他额外的许多功能:

键过期功能,用来实现缓存;
发布订阅功能,用来实现消息系统;
Lua 脚本功能,利用 Lua 脚本创造出新的 Redis 命令;
简单的事务功能,在一定程度上保证事务特性;
流水线 (Pipeline) 功能,客户端能将一批命令一次性传到 Redis,减少网络开销。

(4)简单稳定

代码少,单线程,服务端、客户端处理简单,redis 不依赖操作系统中的类库,自己实现了事件处理的相关功能。

(5)客户端语言多

redis 提供了简单的 TCP 通信协议,很多编程语言可以很方便的接入到 redis。

(6)持久化

redis 提供了两种持久化方式:RDB 和 AOF。可以用这两种策略将内存的数据保存在硬盘中,保证了数据的可持久性。

(7)主从复制

redis 提供了复制功能,实现了多个相同数据的 redis 副本,复制功能是分布式 redis 的基础。

(8)高可用和分布式

redis 提供了高可用实现 Redis Sentinel(哨兵),能够保证 redis 节点的故障发现和故障自动转移。并且 3.0 版本提供了分布式实现 Redis Cluster(集群),
这是 redis 真正的分布式实现,提供了高可用、读写和容量的扩展性。

2、Redis 使用场景

缓存、排行榜系统、计数器应用(redis 天然支持计数功能,且计数性能很好)、社交网络(粉丝、共同喜好、推送等,社交网站的访问量比较大,传统的关系型数据库不太适合保存这种类型的数据,可用 redis 实现)、消息队列系统(消息队列具有业务解耦、非实时业务削峰等特性,redis 可以满足一般的消息队列功能,不过一般项目中还是使用专业的消息队列,更加强大)。

redis 也有不适合它解决的问题场景,站在数据规模和数据冷热角度来分析的话:数据规模角度,数据可分为大规模数据和小规模数据,redis 的数据是放在内存里的,如果数据规模非常大,不适合使用 redis 存储;站在数据冷热角度,数据分为热数据和冷数据,热数据是指需要频繁操作的数据,如果将冷数据放在 redis 中,浪费内存。

二、API 的理解和使用

1、全局命令

(1)查看所有键:keys *

(2)键总数:dbsize

注意:dbsize 命令在计算总数时候不会遍历所有键,而是直接获取 redis 内部的键总数变量,时间复杂度 O(1);而 keys 命令会遍历所有键,时间复杂度 O(n),如果 redis 保存了大量键时,线上环境禁止使用。

(3)检查键是否存在(键存在返回 1,不存在返回 0):exists key

(4)删除键(返回的结果是成功删除的个数,删除一个不存在的键,返回 0):del key [key …]

注意:del key 表示删除一个,del key1 key2 key3 表示删除 3 个

(5)键过期:expire key seconds

注意:redis 支持对键添加过期时间,超过过期时间后,会自动删除键;ttl key 命令会返回键的剩余过期时间,返回值 >= 0 表示键的剩余过期时间,返回值 - 1 表示键没有设置过期时间,返回值 - 2 表示键不存在。

(6)键的数据结构类型:type key

注意:如果键是字符串类型,返回 string,如果键是列表类型,返回 list,其他几种类似。如果键不存在,返回 none。

2、数据结构的内部编码

每种数据结构都有自己底层的内部编码实现,而且是多种实现,redis 会在合适的场景选择合适的内部编码。比如 zset 包含 skiplist 和 ziplist 两种内部编码。这样设计的好处是:可以改进内部编码,而对外的数据结构和命令没有影响;多种内部编码实现可以在不同场景下发挥各自的优势,比如 ziplist 比较节省内存,但是在列表元素比较多的情况下,性能会有所下降,这个时候 redis 会根据配置选项将列表类型的内部实现转换为 linkedlist。

3、单线程架构

redis 使用单线程架构和 I / O 多路复用模型来实现高性能的内存数据库服务。一条命令从客户端达到服务端不会立刻执行,所有命令都会进入到一个队列中,然后逐个被执行。不会有两条命令被同时执行。redis 使用了 I / O 多路复用技术来解决 I / O 的问题。

redis 使用单线程模式那么快的原因:纯内存访问,这个最重要;非阻塞 I /O,redis 使用 epoll 作为 I / O 多路复用技术的实现,并且 redis 加上自身的事件处理模型将 epoll 中的连接、读写、关闭都转换为事件,不在网络 I / O 上浪费过多的时间;单线程避免了线程切换和竞争产生的消耗。

注意:单线程会有一个问题,对于每个命令的执行时间是有要求的,如果执行时间过长,会造成其他命令的阻塞,对于 redis 来说是致命的,所以 redis 是面向快速执行场景的数据库。

4、字符串

(1)设置值:set key value [ex seconds] [px milliseconds] [nx|xx]

注意:nx 键必须不存在,才可以设置成功,用于添加;xx 键必须存在,才可以设置成功,用于更新

(2)获取值:get key

批量设置值:mset key value [key value …]

批量获取值:mget key [key …]

注意:批量操作可以减少网络时间(n 次网络时间 + n 次命令时间 —> 1 次网络时间 + n 次命令时间),但是每次批量操作所发送的命令不是无节制的,如果数量过多可能造成 redis 阻塞或者网络阻塞。

(3)计数:incr key

incr 命令用于对值做自增操作,如果值不是整数,返回错误;如果值是整数,返回自增后的结果;如果键不存在,按照值为 0 自增,返回结果 1。

很多存储系统和编程语言内部使用 CAS 机制实现计数功能,会有一定的 CPU 开销,但 redis 中不存在这个问题,因为 redis 单线程架构,任何命令到了 redis 服务端都要顺序执行。

(4)内部编码(redis 会根据当前值的类型和长度决定使用哪种内部编码实现)

int(8 个字节的长整形) embstr(小于等于 39 个字节的字符串) raw(大于 39 个字节的字符串)

(5)典型应用场景

缓存功能、计数、共享 session、限速

5、哈希

(1)设置值

hset key field value

(2)获取值

hget key field

(3)批量设置或获取 field-value

hmget key field [field …]
hmset key field value [field value …]

(4)内部编码(ziplist、hashtable)

当哈希类型的 field 个数小于 512,并且所有的 value 小于 64 字节,使用 ziplist 作为哈希的内部实现,否则使用 hashtable。512 和 64 是默认的,可以配置。

(5)应用场景

比如:用户信息

6、列表

列表是用来存储多个有序的字符串,可以对列表两端插入和弹出。列表类型有两个特点:(1)列表中的元素是有序的;(2)列表中的元素可以是重复的。

(1)从右端插入、从左端插入

rpush key value [value …] lpush key value [value …]

lrange 0 -1 表示从左到右获取列表的所有元素

(2)内部编码(ziplist、linkedlist、quicklist)

当列表的元素个数小于 512 个,并且列表的每个元素值都小于 64 字节,redis 会选用 ziplist 来作为列表的内部实现,否则选用 linkedlist。Redis3.2 版本提供了 quicklist 内部编码,它是以一个 ziplist 为节点的 linkedlist,结合了 ziplist 和 linkedlist 两者的优势。

(3)使用场景

例如:消息队列、文章列表

7、集合

集合中不允许有重复元素,并且集合中的元素是无序的。

(1)添加元素

sadd key elements [elements …]

(2)删除元素

srem key elements [elements …]

(3)内部编码(intset、hashtable)

当集合中的元素都是整数且元素个数小于 512(默认值,可配置),redis 选用 intset 作为集合内部实现,否则选用 hashtable。

(4)使用场景

标签、社交等,比如一个用户对可乐、体育感兴趣,另一个用户对历史、新闻感兴趣,这些兴趣点就是标签。

8、有序集合

不能有重复成员,元素可以排序,每个元素设置一个分数 (score) 作为排序的依据。有序集合的元素不能重复,但是分数可以重复。

(1)添加成员

zadd key score member [score member …]

(2)内部编码(ziplist、skiplist)

当有序集合的元素个数小于 128 个,并且每个元素的值都小于 64 字节,Redis 使用 ziplist 作为有序集合的内部实现。否则使用 skiplist。

(3)使用场景

排行榜系统

9、健管理

(1)单个健管理

返回键类型、键重命名、键过期、迁移键等。

注意:迁移键有三种方式:move、dump+restore、migrate。move 命令用于 redis 内部进行数据迁移,从一个数据库迁移到另一个数据库,不建议生产环境使用 redis 多数据库功能;dump+restore 可以实现在不同 redis 实例之间进行数据迁移,分为 dump 和 restore 两步,其中在源 redis 上 dump 会将键值序列化,采用 RDB 格式,在目标 redis 上,restore 会将上面序列化的值复原;migrate 命令用于 redis 实例之间进行数据迁移,实际上 migrate 是将 dump、restore、del 三个命令进行了整合。migrate 命令的数据传输直接在源 redis 和目标 redis 上完成。

move 命令作用于 redis 实例内部,是原子性的,不支持多个键;dump+restore 作用于 redis 实例之间,不是原子性,不支持多个键;migrate 作用于 redis 实例之间,是原子性的,支持多个键。

(2)遍历键(keys 和 scan)

keys 会全量遍历所有键,可能造成 redis 阻塞。scan 可以想象成只扫描字典中的一部分键,直到将字典中的所有键遍历完毕。scan 可以有效解决
keys 命令可能产生的阻塞问题,但是 scan 的过程中,如果有键的变化(增加、删除、修改),就可能新键没有遍历到或者遍历了重复的健。所以 scan 不能保证完整的遍历出来所有的健。

(3)数据库管理

select dbIndex 切换数据库

flushdb/flushall 清除数据库,flushdb 只清除当前数据库,flushall 清除所有数据库。

正文完
 0