01 Redis 初识
Redis 是一个远程内存数据库,它不仅性能强劲,而且还具有复制特性以及为解决问题而生的独一无二的数据模型。Redis 提供了 5 种不同类型的数据结构,各式各样的问题都能很自然地映射到这些数据结构上。通过复制、持久化(persistence)、客户端分片(client-side sharding)等特性,用户可以很方便的将 Redis 扩展成一个能够包含数百 GB 数据每秒处理上百万次请求的系统。
1.1 Redis 简介
Redis 是一个速度非常快的非关系型数据库(non-relational database),它可以存储键(key)与 5 种不同类型的值(value)之间的映射(mapping),可以将存储在内存的键值对数据持久化到硬盘,可以复制特性来扩展读性能,还可以使用客户端分片来扩展写性能。
Redis 与其它数据库或软件的对比
相比于关系型数据库中两个表的关联,Redis 不使用表,它的数据库也不会预定义或强制去要求用户对 Redis 的不同数据进行关联;
高性能键值缓存服务器 memcached 经常被拿来与 Redis 进行比较:
- 这两者都可用于存储键值映射,彼此性能相差无几;
- 但是 Redis 能够自动以两种不同的方式将数据写入磁盘;
- 并且 Redis 除了能存储普通的字符串键以外,还可以存储其它 4 种数据结构,而 memcached 只能存储普通的字符串键。
这些不同之处使得 Redis 可以用于解决更为广泛的问题,并且既可以用作主数据库(primary database)使用,也可以作为其它存储系统的辅助数据库(auxiliary database)。
一般来说,许多用户只会在 Redis 的性能或功能是必要的情况下,才会使用 Redis。读者应该根据自己的需求来决定是否使用 Redis ,并考虑是将 Redis 作为主存储还是辅助存储,以及如何通过复制、持久化、事务等手段保证数据的完整性。
名称 | 类型 | 数据存储选项 | 查询类型 | 附加功能 |
---|---|---|---|---|
Redis | 使用内存存储(in-memory)的非关系型数据库 | 字符串、列表、集合、散列表、有序集合 | 每种类型都有自己的专属命令,另外还有批量操作(bulk operation)和不完全(partial)的事务支持 | 发布与订阅,主从复制(master/slave replication),持久化,脚本(存储过程,stored procedure) |
Memcached | 使用内存存储的键值缓存 | 键值之间的映射 | 创建命令、读取命令、更新命令、删除命令以及其它几个命令 | 为提升性能而设的多线程服务器 |
Mysql | 关系型数据库 | 每个数据库可以包含多个数据表,每个表可以包含多个行;可以处理多个表的视图;支持空间和第三方扩展 | SELECT、UPDATE、INSERT、DELETE、函数、存储过程 | 支持 ACID 性质(需要 InnoDB),主从复制和主主复制 |
MongoDB | 使用硬盘存储(on-disk)的非关系文档存储 | 每个数据库可以包含多个表,每个表可以包含多个无 schema(schema-less) 的 BSON 文档 | 创建命令、读取命令、更新命令、删除命令、条件查询命令等 | 支持 map-reduce 操作,主从复制,分片,空间索引 |
附加特性
持久化:
在使用类似 Redis 这样的内存数据库时,一个首先需要考虑的问题就是 “当服务器被关闭时,服务器存储的数据将何去何从呢?”
Redis 有两种不同的持久化方法,他们都可以以一种小而紧凑的格式将存储在内存中的数据写入硬盘。
第一种为时间点转储(point-in-time dump),转储操作既可以在“指定时间段内有指定数量的写操作执行”这一条件被满足时执行,又可以通过调用两条转储的硬盘命令中的任何一条来执行;
第二种持久化的方法将所有修改了的数据库的命令写入一个只追加的文件里面,用户可以根据数据的重要程度,将只追加写入设置为从不同步、每秒同步一次或者每写入一个命令就同步一次。
主从复制:
另外,尽管 Redis 的性能很好,但是受限于内存存储设计,有时候一台 Redis 服务器可能没办法处理所有请求。为了扩展 Redis 的读性能,并为 Redis 提供故障转移支持,Redis 实现了主从复制的特性。
主从复制:执行复制的从服务器会连接上主服务器,接受主服务器发送的整个数据库的副本;之后主服务器执行的写命令,都会被发送给所有连接着的从服务器去执行,从而实时地更新服务器的数据集。因为从服务器包含的数据会不断地进行更新,所以客户端可以向任意一个从服务器发送读请求,以此来避免对主服务器进行集中式的访问。
使用 Redis 的理由
- 在删除元素上 Memcached采用的办法是通过黑名单机制(blacklist)来隐藏列表里的元素,从而避免对元素的读取、更新、写入等操作,相反,Redis 的 LIST 和 SET 允许用户直接添加或删除元素。
- 使用 Redis 不仅可以让代码更加简介、更易懂、更易维护,而且可以使代码运行速度更快(因为用户不需要通过读取数据库来更新数据)。此外 Redis 效率和易用性也比关系型数据库好很多。
- 数据库对表里面的行进行更新是一个速度很慢的操作,除了会引起一次随机读之外,还会引起一次随机写。而在 Redis 里面,用户可以直接使用原子的 INCR 命令以及其变种来计算聚合数据,并且因为 Redis 是将数据存储在内存里面,而且发送给 Redis 命令请求不需要经过经典的查询分析器和查询优化器进行处理,所以对 Redis 存储的数据执行随机写的速度是非常迅速的。
- 此外,因为 Redis 不是关系型数据库或是其它硬盘存储数据库,可以避免写入不必要的临时数据,也免去了对临时数据进行扫描或删除的麻烦,并最终改善程序的性能。
1.2 Redis 数据结构简介
Redis 可以存储键与 5 种不同数据结构类型之间的映射,分别是 string、list、set、hash、zset。有一部分命令对这五种数据结构是通用的,例如 del、type、rename 等;但也有一部分 Redis 命令只能对其中一种或两种结构使用。
结构类型 | 结构存储的值 | 结构的读写能力 |
---|---|---|
string | 字符串、整数、浮点数 | 对整个字符串或者字符串种的一部分执行操作;对整数和浮点数执行自增或者自减操作 |
list | 一个链表,链表上的每个节点都包含了一个字符串 | 从链表的两端推入或者弹出元素;根据偏移量对链表进行修剪;读取单个或者多个元素;根据值查找或者移除元素 |
set | 包含字符串的无序收集器,并且被包含的每个字符串都是独一无二、各不相同的 | 添加、获取、移除单个元素;检查一个元素是否存在于集合中;计算交集、并集、差集。从集合里面随机获取元素 |
hash | 包含键值对的无序散列表 | 添加、获取、移除单个键值对;获取所有键值对 |
zset | 字符串成员与浮点数分值之间的有序映射,元素的排列顺序由分值大小决定 | 添加、获取、移除单个元素;根据分值范围或者成员来获取元素 |
注意:
- 以下将使用 Docker 搭建的简单 Redis 环境,使用 redis-cli 进行简单的命令交互。Docker 启动 Redis 过程见本文最下面附录。
- 以下为五种数据结构的最简单操作。
Redis 中的字符串 string
命令 | 行为 |
---|---|
get | 获取存储在键中的值 |
set | 设置存储在键中的值 |
del | 删除存储在给定键的值 |
Redis 中的列表 list
一个列表结构可以有序的存储多个字符串。
命令 | 行为 |
---|---|
rpush | 将指定的值推入到列表的右端,该命令返回列表当前长度 |
lrange | 获取列表在给定范围的所有值。使用 0 为开始索引, -1 为结束索引可以取出所有元素 |
lindex | 根据 index 取出单个元素 |
lpop | 从列表的左端弹出一个值,并返回被弹出的值 |
Redis 中的集合 set
Redis 的集合和列表都可以存储多个字符串,他们之间的不同在于,列表可以存储多个相同的字符串,而集合则通过使用散列表来保证自己存储的每个字符串都是各不相同的(这些散列表只有键,没有与键相关联的值)。
命令 | 行为 |
---|---|
sadd | 将指定元素添加到集合 |
smembers | 返回集合中包含的所有元素 |
sismember | 检查给定的值是否存在于集合中 |
srem | 如果给定的元素存在于集合中,移除这个元素 |
集合除了基本的添加操作和移除操作外,还支持很多其它操作,比如 sinter、sunion、sdiff 这三个命令就可以分别执行交集运算、并集运算、差集运算。
基本操作:
Redis 中的散列表 hash
Redis 中的散列可以存储多个键值对之间的映射。散列存储的值可以是字符串或者是数字,并且用户同样可以对其数字类型的值进行自增、自减操作。
散列在很多方面类似于一个微缩版的 Redis。
命令 | 行为 |
---|---|
hget | 获取指定散列键的值 |
hset | 在散列里面关联起给定的键值对 |
hgetall | 获取散列包含的所有键值对 |
hdel | 如果给定键存在于散列里面,移除这个键 |
基本操作:
Redis 中的有序集合 zset
有序集合于散列一样,都用于存储键值对:
有序集合的键被称为成员(menber),每个成员都是各不相同的;
有序集合的值被称为分值(score),分值必须为浮点数。
有序集合是 Redis 里面唯一一个可以根据成员访问元素,又可以根据分值以及分值的排列顺序来访问元素的结构。
命令 | 行为 |
---|---|
zadd | 将一个带有给定分值的成员添加到有序集合里面 |
zrange | 根据元素在有序集合中所处的位置,从有序集合里面获取多个元素 |
zrangebyscore | 获取有序集合在给定分值范围内的所有元素 |
zrem | 如果给定成员存在于有序集合,那么移除这个成员 |
基本操作:
1.3 简单小结
Redis 是一个可以用来解决问题的工具。,它既拥有其它数据库不具备的数据结构,又拥有内存存储(这使得 Redis 的速度非常快)、远程(这使得 Redis 可以与多个客户端和服务器进行连接)、持久化(这使得服务器可以在重启之后仍然保持重启之前的数据)和可扩展(通过主从复制和分片)等多个特性,这使得用户可以以熟悉的方式为何种不同的问题构建解决方案。
思维的转变:
“怎样将我的想法塞进数据库的表和行里面” 将转变为:“使用哪种 Redis 数据结构来解决这个问题比较好呢”。