Redis 入门指南(第 2 版)读书笔记

572 阅读10分钟

Redis 入门指南(第 2 版)读书笔记

读书笔记《Redis 入门指南》 李子骅. Redis 入门指南(第 2 版)(异步图书) (Chinese Edition) (Kindle Locations 627-632). 人民邮电出版社. Kindle Edition.

前阵子部门团建要去外地,路上大巴车来回有将近 6、7 个小时的空闲时间,排除和身边人玩 Switch 的时间,大部分时间都用来速读一本纯技术类工具书,《Redis 入门指南(第 2 版)》,粗略自己摘录并且记录一些读书笔记,记录于此,以备自己方便查阅。内容可以较多但仅限个人记录一定有所疏漏,还是建议使用到 Redis 的开发者朋友,使用 Dash 或者其他查看 API 一类工具放手边随时查阅。

转岗做前端之前,我是一名 Java 服务端工程师,最早接触 Redis 时,是在上个部门,将 Redis 作为缓存使用(在来京东之前,只使用过 memerorycache 以及 H2 内存数据库,Oracle、MySQL, MSSQL 水平一般,大部分使用 Hibernate 封装,加入京东以后基本使用 iBatis + MySQL ),最初使用 Redis 非常初级,直接调用被封装好的 API,基本使用到 GET 和 SET 操作,当时没有太深入,速读便于回顾。

介绍

Redis (Remote Dictionary Server)顾名思义他的存储形式是以字典形式存储结构,并且允许其他应用通过 TCP 协议进行读写操作。

Redis 数据库中的所有数据都存储在内存中。由于内存的读写速度远快于硬盘,因此在读写性能上对比其他基于硬盘存储的数据库系统有非常明显的优势,按照常理,存储在内存存在一个问题,就是一旦程序退出或者硬件断电,就会导致数据丢失,不过 Redis 提供了数据持久化到硬盘的方案支持,可以异步写入硬盘,同时不影响对客户端提供数据服务。

安装与启动/停止

OS X

brew install redis

Linux 等

wget http://download.redis.io/redisstable.tar
tar xzf redis-stable.tar.gz
cd redis-stable
make

默认端口:6379

文件名说明
redis-server服务
redis-cli命令行客户端
redis-benchmark性能测试工具
redis-check-aofAOF 文件修复工具
redis-check-dumpRDB 文件检查工具
redis-sentinelSentinel 服务器(v2.8 后)

停止服务,Redis 会先断开客户端连接,然后根据配置持久化数据,最后退出。

启动时可以自定义配置 redis-server path/to/redis.conf

多数据库支持

Redis 默认支持 16 个数据库,不可以自定义数据库名,只能根据编号命名,默认从 0 开始。

不支持为每一个数据库设置密码,一个客户端要么能访问所有库,要么没权限访问。

数据类型

Redis 与 MySQL 等关系型数据库以二维表形式的存储有非常大的差异,Redis 是 NoSQL 中一员,采用 key - value 方式存储,它的键值支持存储的类型有下面集中类型:

  • 字符串类型
  • 散列类型
  • 列表类型
  • 集合类型
  • 有序集合类型

字符串类型

key 和 value 都是字符串

SETGET

提示 Redis 对于键的命名并没有强制的要求,但比较好的实践是用“对象类型:对象 ID:对象属性”来命名一个键,如使用键 user:1:friends 来存储 ID 为 1 的用户的好友列表。对于多个单词则推荐使用“.”分隔,一方面是沿用以前的习惯(Redis 以前版本的键名不能包含空格等特殊字符),另一方面是在 rediscli 中容易输入,无需使用双引号包裹。另外为了日后维护方便,键的命名一定要有意义,如 u:1:f 的可读性显然不如 user:1:friends 好(虽然采用较短的名称可以节省存储空间,但由于键值的长度往往远远大于键名的长度,所以这部分的节省大部分情况下并不如可读性来得重要)。

散列类型

Hash,每一个键存储字段不同。

HSET key field value
HGET key
HMSET key field value [field value ...] # 存储某一个key,某一个或多个字段的 value
HMGET key field [field ...] # 获取某一key的某一个或多个value
HGETALL key # 获取某一 key 的所有value

自由地为任何键增减字段而不影响其他键。

列表类型

KEYS 命令需要遍历数据库中所有键,出于性能考虑,一般很少在生产环境使用。

List 可以存储一个有序的字符串列表,内部是 双向链表 实现的,所以在列表两端增加元素时间负责度都是 O(1),获取越接近两端的元素速度越快。链表的缺点是通过索引来访问某一个元素慢。

这种特性使列表类型能非常快速地完成关系数据库难以应付的场景:如社交网站的新鲜事,我们关心的只是最新的内容,使用列表类型存储,即使新鲜事的总数达到几千万个,获取其中最新的 100 条数据也是极快的。同样因为在两端插入记录的时间复杂度是 O(1),列表类型也适合用来记录日志,可以保证加入新日志的速度不会受到已有日志数量的影响。

LPUSH key value [value ...] # 向列表左边追加
RPUSH key value [value ...] # 向列表右边追加

LPOP key # 从列表左边弹出一个元素
RPOP key # 从列表右边弹出一个元素

LLEN key # 获取列表元素数量

LRANGE key start stop # 获取列表某一段,**!!常用!!**

集合类型

集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,由于集合类型在 Redis 内部是使用值为空的散列表(hashtable)实现的,所以这些操作的时间复杂度都是 O(1)。最方便的是多个集合类型键之间还可以进行并集、交集和差集运算...

SADD key member [member] # 添加
SREM key member [member] # 删除

SMEMBERS key # 获取key集合中所有元素
SISMEMBER key member # 判断某member元素是否在key集合中 **!!效率极高,O(1)!!**

SDIFF key [key ,,] # 集合差集
SINTER key [key ,,] # 集合交集
SUNION key [key ,,] # 集合并集

有序集合

区别于列表类型:

(1)列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会较慢,所以它更加适合实现如“新鲜事”或“日志”这样很少访问中间元素的应用。(2)有序集合类型是使用散列表和跳跃表(Skiplist)实现的,所以即使读取位于中间部分的数据速度也很快(时间复杂度是 O(log(N)))。(3)列表中不能简单地调整某个元素的位置,但是有序集合可以(通过更改这个元素的分数)。(4)有序集合要比列表类型更耗费内存。

ZADD key score member [score member ...] # 增加元素
ZSCORE key member # 获取

ZRANGE key start stop [WITHSCORES] # 获取有序的某一段(从小到大)
ZREVRANGE key start stop [WITHSCORES] # 获取有序的某一段(从大到小)


ZRANGEBYSCORE key min max [WITHSCORES] [LIMIToffsetcount] # 按照元素从小到大顺序返回 min 和 max 之间的元素

事务

关系型 SQL 数据库一个非常大的特点就是事务的特性,同样也是 Redis 命令的最小的执行单元,一个事务要么执行,要么不执行。(关系型事务,多个表操作,同一个事务内要么都成功,要么都失败。)

MULTI
# xxx 命令
EXEC

Redis 事务可以保证一个事务内的命令依次执行而不被其他命令插入。

Redis 事务的异常处理,首先需要先明确什么原因导致执行出错。1)语法错,一旦前面有错,后面不会执行;2)运行错,一旦有错,后续的命令会继续执行;

Redis 的事务没有关系数据库事务提供的回滚(rollback)[1]功能。为此开发者必须在事务执行出错后自己收拾剩下的摊子(将数据库复原回事务执行前的状态等)。

WATCH 命令,WATCH 命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行。

过期时间

EXPIRE key seconds # 单位是秒,过期时间到了以后Redis自动删除key
PEXPIRE key ms # 单位毫秒
TTL key # 剩余到期时间,单位是秒

排序:SORT 命令,BY 参数,GET 参数,STORE 参数

消息通知,任务队列

生产/消费者模式,分发消息以及任务队列的实现,可以借助 Redis。

使用 Redis 实现简单的任务队列,一边 LPUSH,一边 RPOP 即可。(BRPOP 命令)

优先级队列,BRPOP key [key ...] timeout

发布/订阅模式

PUBLISH channel message
SUBSCRIBE channel

存在规则订阅,PSUBSCRIBE 命令。

节省空间方法:1)精简键名和键值;2)内部编码优化

脚本

Lua 语言(Open Rest,Nginx 也可以使用 lua 语言,有机会学习了解一下)

这里不详细记录 Lua 语法,不过有一点思考,既然 Nginx 也可以使用 Lua,那么可以就有一种场景,Nginx 通过 lua 访问 Redis 读取数据,并且用 lua 渲染模板,达到页面直出,这样应该效率很高。

持久化

  • RDB (通过快照完成,当达到某种约定条件后自动生成一份备份并存储在硬盘上),快照原理
  • AOF (存储非临时数据,每执行一条都会追加存储在硬盘,有一些性能影响)默认关闭,通过 appendonly yes 开启

允许同时开启 RDB 和 AOF 两种模式。

集群

Redis 支持集群,可以通过主从数据库来来规避单点数据库故障导致的问题。主数据库负责读写(读写分离也可以),当写操作导致数据变化时自动将数据同步给从库,从库只读,并只接受主库同步数据。

配置文件,通过 slaveof 主库地址 主库端口 来完成主从复制的配置。

通过复制可以实现读写分离,以提高服务器负载能力。

关键字记录,Redis 支持哨兵,一主多从,需要自动监控 Redis 运行情况,作用:1)监控主从数据库运行正常;2)主数据库故障自动将从数据库转换成主数据库;细节待补充一篇琢磨透彻一点的分析文。

Redis 3.0 支持集群一大特点,还在学习中,这里不做细描述。(作者自己还一知半解,需要点时间和资料消化消化)

Redis 管理(偏 OPS 运维)

默认 Redis 允许所有连接,可以通过 bind 绑定某一地址,可以设置数据库密码,提升安全性。

将命令重命名,确保部分影响性能或整个数据库的命令,被自定义成一个新命令。

结语

个人拙见:

  1. 在速读的过程中,有几个想法,数据层面凭我个人经验来讲,可以分为热数据和冷数据,那么,其实无论是 Redis 这样 NoSQL 还是与 MySQL 这样各有专长的关系型数据库,可以组合来实现对前台数据的持续服务,冷数据来源于关系型,热数据来源于冷数据,定时按照 一定策略 进行更新与同步,这样既可以保证数据存储,也可以保证读写的高效