Redis入门指南

什么是Redis?

Redis 是一个作为“数据结构服务器”来使用的开源工具,它可以存储不同的数据类型并可被快速的存取,因为数据类型的值存储在内存中. 如果你已经使用过memcached就会发现Redis也是一个类似的工具,不过它支持更多的数据类型.

什么时候用Redis

Redis 通常被用作数据的辅助存储,也就是说,除了Redis外,你应该还要有个主数据库 (如: PostgreSQL 或关系型数据库) . Redis用于存储短暂的数据或可被快速存取的缓存数据以及可被重建的数据,如session数据. Redis默认不会将数据持久保存, 虽然可以配置是否持久保存所要的数据,所以严格不允许丢失的数据就不好放Redis里. 同时,由于它是一个内存型数据存储, 所以它可存储的数据量取决于可用的内存大小.

Redis数据类型

Redis 主要以有下数据类型:

  • string: 基本的键值对.
  • hashes: 这个类型的值本身就是一些键值对,主要用于存储对象.
  • lists: 以特定的顺序存放多个值,可以很好的用于只从列表的一端(称为“头”和“尾”)添加或移除元素.
  • sets: 无序存储多个唯一的值. 你可以在无性能损耗的情况下添加,移除和检查其中的任何元素,但是不能添加重复的元素.
  • sorted sets: 集合中的每个值都有一个”分数”,数据按“分数”排好序,以便在使用的时候可以快速获取到.

安装和使用Redis

https://redis.io/download这个页面有在各平台上安装Redis的说明,这页面是被积极维护的. 你也可以用平时的安装包管理器(在OS Xh 上是brew,在Linux上是yum或apt)来获得“redis”的安装包. Redis有一些依赖库,但官方不维护这些在Windows上使用的依赖.

为了使用Redis,你要选择一个属于你自己技术栈的客户端. https://redis.io/clients这个页面列出了所有可用的客户端, 下面这些是今天比较流行的web编程语言:

Ruby: https://github.com/redis/redis-rb

PHP: https://github.com/nrk/predis

Python: https://github.com/andymccurdy/redis-py

NodeJS: https://github.com/NodeRedis/node_redis

Redis命令

你可以通过具体的语言库或自带的 redis-cli 来使用Redis命令. 命令本身不区分大小写,但显示时通常被转成大写的方式.

有一些命令是没有具体类型前缀的. 像这些用于检查或存储数据的命令:

MONITOR 显示服务器上发生的每个动作. 这对调试非常有用,但对繁忙的服务器开启这个命令对服务器性能就有影响.

INFO 显示当前Redis的配置.

KEYS [pattern] 找出所有与模式相匹配的键. 你也可以使用通配符如 ? 来匹配单个字符或使用 * 来匹配任意值.

> MSET hat blue bag red

OK

> KEYS *

1) "hat"

2) "bag"

> KEYS h*

1) "hat"

> KEYS ?a?

1) "hat"

2) "bag"

SCAN cursor [match PATTERN] [COUNT count] 迭代并返回与之匹配的所有的键,通过使用游标来分页. 通过 SCAN可以避免因KEYS 查询需返回大量数据所带来的性能问题. 它的第一个返回值是游标的下一个值, 这个值用于获取下一批匹配结果. 例如, 在一个有21个键的集合上:

> scan 0

1) "9"

2) 1) "key:18"

2) "key:13"

3) "key:7"

4) "key:15"

5) "key:5"

6) "key:1"

7) "key:14"

8) "key:8"

9) "key:12"

10) "key:4"

> scan 9

1) "31"

2) 1) "key:20"

2) "key:3"

3) "key:21"

4) "key:19"

5) "key:2"

6) "key:6"

7) "key:9"

8) "key:16"

9) "key:17"

10) "key:11"

> scan 31

1) "0"

2) 1) "key:10"

SCAN 也有一些姊妹类命令 HSCAN, SSCAN和 ZSCAN ,这些命令将在后面的hash, set和 sorted set 章节被各自讲到.

TYPE [key] 返回存储在一个特定键中的数据类型信息. 这对为找出一个特殊的键能够与什么命令前缀配合使用非常有用.

命令前缀

Redis命令有时会因数据类型的不同而有不同的表现. 为了明示命令的含义,通常命令前面的第一个字符代表它能作用于什么数据类型上. 这里有一些需要留意的:

H开头的代表hash类的命令; S 代表set类的命令; Z 代表已排序的set(因为S已经被用过了). L 代表对list进行左端操作.显然,R代表对list右端进行操作.

还有些其它的转换如使用 M 来代表对多个值的操作处理,B代表阻塞操作.

命名空间键

Redis里的键可以只是简单的strings; 它没有将相关键放到一起的内建方式. 但通常我们使用分号来作为键的分隔符. 这对使用 KEYS 命令来找出与特定模式相匹配的键时非常的方便. 下面是一些例子:

product:98632

user:janebloggs

article:42:views

article:42:categories

更具有自我描述性或自我组织组的键可更易用于查找数据. 也可更容易判别不再需要的数据. 由于Redis是一个内存型的数据存储器, 所以清理数据是要非常小心! 更多的关于过期键的信息会在”让数据持久化和过期”这节中详细讲到.

键值命令

让我们从简单的开始! SET 用于设置一个值; GET 用于获取一个值; GETSET 设置一个值然后获取前一个值:

> SET name Alice

OK

> GET name

"Alice"

> GETSET name Bob

"Alice"

> GET name

"Bob"

由于一次使用一个键效率很低,所以Redis提供了一次设置和读取多个项的命令— MGET 获取多个键值对 MSET 设置多个键值对:

> MSET fruit apple cookie choc-chip

OK

> MGET cookie fruit

1) "choc-chip"

2) "apple"

如果你要做数值统计或与数值相关的计算,那么有些很简单快捷的命令可以做到这点.

INCR 和 DECR 自增/自减一个值 (如果键不存在就创建它); INCRBY 和 DECRYBY 从当前的值中增加/减少指定值.

> GET counter

(nil)

> INCR counter

(integer) 1

> GET counter

"1"

> INCRBY counter 3

(integer) 4

> DECR counter

(integer) 3

> GET counter

"3"

Redis使用string类型来支持其它的特性如位图. 位图是将单独的位写到string里并紧凑存储,例如, 一些用于特殊项的boolean字段. string可被看作是有2^32 个位被设置为了0.

GETBIT 获取位图中特殊的位; SETBIT 设置一个特殊的位到位图.

> SETBIT prefs 5 1

(integer) 0

> SETBIT prefs 3 1

(integer) 0

> GETBIT prefs 100

(integer) 0

> GETBIT prefs 3

(integer) 1

Hash命令

Hash命令存储多个字段作为一个特殊键的值. 例如. 通常使用hash来存储一个具有属性的对象,这个对象可用于设置多个字段.

读写单个字段对HSET 和 HGET 命令而言是非常容易的, 而且它还支持一个命令处理多个字段.

HSET 设置字段到hash; HMSET 设置多个字段到hash; HGET 从hash中获取字段; HMGET 从hash中获取多个字段:

> HSET user:alice name alice

(integer) 1

> HMSET user:alice dress blue food mushrooms

OK

> HGET user:alice food

"mushrooms"

探究hash的不同方式:

HLEN 返回hash中字段的个数.

HKEYS: hash中的字段.

HVALS: hash中字段的值.

HGETALL 返回键和字段值的组合.

HEXISTS 检查字段是否存在hash中.

> HMSET user:belle dress yellow name belle food cake

OK

> HLEN user:belle

(integer) 3

> HKEYS user:belle

1) "dress"

2) "name"

3) "food"

> HVALS user:belle

1) "yellow"

2) "belle"

3) "cake"

> HGETALL user:belle

1) "dress"

2) "yellow"

3) "name"

4) "belle"

5) "food"

6) "cake"

> HEXISTS user:belle shoes

(integer) 0

我们也可以使用HSCAN 命令来检查hash, 这对大hash或查找hash里的具体的字段组特别有用. 就像前面提到的SCAN命令所工作的那样,使用游标可对结果数据进行适当的分页.

HSCAN 找出hash和hash里的字段:

> HSCAN user:ariel 0

1) "0"

2) 1) "name"

2) "ariel"

3) "superpower"

4) "mermaid"

5) "dress"

6) "green tail"

7) "sisters"

8) "6"

> HSCAN user:ariel 0 MATCH s*

1) "0"

2) 1) "superpower"

2) "mermaid"

3) "sisters"

4) "6"

List命令

Redis里的列表是一个用链式存储的数据结构. 它可以非常方便的操作仅在列头或列尾附近的值,. 在Redis里, 对列表进行”左”端 和”右”端操作的命令前缀分别为 L 和 R . 通过PUSH 命令给列表添加元素,通过POP 命令从列表末端移除并返回元素.

在Redis里列表可用来实现栈或队列.

LPUSH 添加一个新的值到列表的左端. RPUSH 添加一个新的值到列表的右端. LRANGE 返回列表的一些项, 指定返回多少个并从哪里开始.

> LPUSH rhyme little

(integer) 1

> LPUSH rhyme twinkle

(integer) 2

> LPUSH rhyme twinkle

(integer) 3

> RPUSH rhyme star

(integer) 4

> LRANGE rhyme 0 -1

1) "twinkle"

2) "twinkle"

3) "little"

4) "star"

> LRANGE rhyme 1 2

1) "twinkle"

2) "little"

LPOP 移除列表最左端的值并返回它. RPOP 移除列表最右端的值并返回它. LLEN 返回列表中有多少项.

> LLEN rhyme

(integer) 4

> RPOP rhyme

"star"

> LPOP rhyme

"twinkle"

> LRANGE rhyme 0 -1

1) "twinkle"

2) "little"

LSET 将值设定到列表的特定位置(位置必须位于当前列表索引下标的合法范围内). LINDEX 从列表左端或右端的获取索引值. LINSERT 将值插入到列表的任意位(从左端附近插入效果最好,因为这不需要遍历太多的项).

> LPUSH rainbow yellow

(integer) 1

> LPUSH rainbow orange

(integer) 2

> LPUSH rainbow red

(integer) 3

> LSET rainbow 1 green

OK

> LINDEX rainbow 2

"yellow"

> LINSERT rainbow BEFORE green amber

(integer) 4

> LRANGE rainbow 0 -1

1) "red"

2) "amber"

3) "green"

4) "yellow"

LTRIM 截取列表到指定的大小 — 对短而小的缓冲很有用:

> LPUSH recent_orders latte

(integer) 1

> LPUSH recent_orders chai

(integer) 2

> LPUSH recent_orders smoothie

(integer) 3

> LRANGE recent_orders 0 -1

1) "smoothie"

2) "chai"

3) "latte"

> LTRIM recent_orders 0 1

OK

> LRANGE recent_orders 0 -1

1) "smoothie"

2) "chai"

> LPUSH recent_orders mocha

(integer) 3

> LTRIM recent_orders 0 1

OK

> LRANGE recent_orders 0 -1

1) "mocha"

2) "smoothie"

Set命令

Redis有set和sorted set命令, 它们很相似,但是是两个不同的命令. 这节先介绍非排序的类型先,稍后再详细介绍已排序的set. Sets的键都是唯一的,非常适合存储不重复的值.

SADD 将值添加到集合里(如果已经存在,就不会再被添加). SMEMBERS 显示集合的所有元素. SSCAN 检查一个集合的内容; 就像 SCAN 工作的那样,使用一个游标和可选的模式来匹配:

> SADD post:1:tags tech

(integer) 1

> SADD post:1:tags javascript

(integer) 1

> SADD post:1:tags tips

(integer) 1

> SADD post:1:tags couchdb

(integer) 1

> SADD post:2:tags couchdb

(integer) 1

> SADD post:2:tags pouchdb

(integer) 1

> SADD post:2:tags pouchdb

(integer) 0

> SMEMBERS post:2:tags

1) "pouchdb"

2) "couchdb"

> SSCAN post:2:tags 0

1) "0"

2) 1) "pouchdb"

2) "couchdb"

SUNION 返回多个集合的并集(不包括重复的元素). SINTER 返回多个命令的交集. SINTERSTORE 与 SINTER 一样,但它只是将结果存储在命名的键中而不返回:

> SINTER post:1:tags post:2:tags

1) "couchdb"

> SINTERSTORE overlap post:1:tags post:2:tags

(integer) 1

> SMEMBERS overlap

1) "couchdb"

> SUNION post:1:tags post:2:tags

1) "couchdb"

2) "tips"

3) "javascript"

4) "pouchdb"

5) "tech"

SPOP 从集合中随机移除并返回一个元素, SRANDMEMBER 随机返回集合中的一个元素但不删除它:

> SADD coin heads

(integer) 1

> SADD coin tails

(integer) 1

> SRANDMEMBER coin

"tails"

> SRANDMEMBER coin

"heads"

> SRANDMEMBER coin

"heads"

> SPOP coin

"heads"

> SMEMBERS coin

1) "tails"

Sorted Set 命令

Sorted sets 看上去有点像sets, 但实际上,在许多的用途中,它们的差别很小. 元素在集合中被一个接一个的存储,但sorted set对计数,显示板和其它短期的统计任务非常有用. 因为sorted set的排序默认是升序, 许多能够与sorted set配合使用的命令 (所以与 Z开头的命令) 都有相应的反序“姊妹”命令. 反序意味着最高“得分”的项排在列表的最前面.

ZADD 将一个带有“分数”的值添加到sorted set里. ZINCR 增加sorted set里一个特定值的“得分” (根据需要创建集合和值). ZRANGE 以“得分”的顺序获取集合里的部分或所有的元素 (最小“得分”元素排在最前). WITHSCORES 将“分数”作一个额外的值放到元素的后面一并返回. ZREVRANGE 与 ZRANGE 有点类似,但它是按最高“得分”排在最前来排序的,对于一些关于阅览次数最多/评论最多/投票最多的特性非常有用:

> ZADD product_views 1 table

(integer) 1

> ZINCRBY product_views 1 bench

"1"

> ZINCRBY product_views 1 bench

"2"

> ZINCRBY product_views 1 wheelbarrow

"1"

> ZRANGE product_views 0 -1

1) "table"

2) "wheelbarrow"

3) "bench"

> ZREVRANGE product_views 0 0 WITHSCORES

1) "bench"

2) "2"

ZSCORE 获取特定值当前“得分”. ZRANK 如果集合的元素已按“得分”排列了,这命令将用于找出一个值的位置,位置从0开始. ZREVRANK 找出一个值的位置,位置从0开始,如果排名是按“得分”从高到低排列的话:

> ZINCRBY product_views 1 bench

"3"

> ZSCORE product_views bench

"3"

> ZRANK product_views bench

(integer) 2

> ZREVRANK product_views bench

(integer) 0

让数据持久保存或过期

通过前面的接触,我们知道保持清除Redis数据的重要性. 其中的一种方式就是给键设定一个特定长时间的过期值以便在存储空间不够用时被清除. 反之,有时候你真的需要保持你的数据. 在这节中,我们也会了解通过更改Redis的配置允许部分或所有的数据从程序启动以后一直存活.

让键过期

设置键过期可做到大部分存储空间对Redis可用,因为收回了旧数据所占据的空间. 要注意的是,只能通过对键而不是值来设置过期, 所以设计数据结构时就要考虑过期策略.

EXPIRE 设置键可以存活多长时间(以秒为单位); 过了这个存活期,键就会被删除. SETEX 就是将SET 和 EXPIRE 组合起来的命令,因为这两个命令经常被一起使用. EXPIREAT 定义了键在什么时候(格式为Unix时间戳)应该被删除 . TTL 定义直到键被过期的时间值, 若值为 -1,则它被设置为不过期,若值为-2,则此键根本就不存在. PERSIST 移除键的过期值以便它不会被自动删除:

> SET temp_value 42

OK

> EXPIRE temp_value 10

(integer) 1

> TTL temp_value

(integer) 3

> TTL temp_value

(integer) -2

> SET temp_value 42

OK

> EXPIRE temp_value 10

(integer) 1

> PERSIST temp_value

(integer) 1

> TTL temp_value

(integer) -1

配置持久存储

默认情况下,Redis不会持久保存数据. 通过额外的设置可以让Redis每隔一段时间将快照保存到磁盘上, 但放心的是,在服务器失效前它是永远不会立即发生的!这通常取决于设计; Redis是一个非常快的数据存储,因为它将数据存储在内存中而不是写到磁盘上. 也就是说, 有些时候还是期望能将数据持久存储,而不是从来都不可能不这样做.

Redis 的两种持久化选项:

  1. 周期性的快照, 执行快照的时间间隔根据需求进行配置. 要注意的是,在繁忙的系统中执行频繁的快照对性能会有影响. 快照也是实现Redis数据备份的一种方式. 技术层面就是通过RDF (Redis数据文件)这个选项来设定.
  2. 事务更改日志将每个Redis命令写到文件中, 以指定的间隔(可配置)在后台将命令同步到磁盘上. 这个 AOF (只追加文件) 方式可以非常好的被用于捕捉事务变化,同时也可以作为一个可被解析的日志来使用.

在实践中,多数Redis安装都会结合使用这两种方式来获得更好的性能或数据恢复. 这里有一个非常好的文档 (https://redis.io/topics/persistence) 详细地讲解了如何使用RDF文件作备份以及如何恢复数据.

Redis的发布与订阅模型

Redis自带易使用的发布/订阅. 订阅者监听一个通道,而发布者将消息广播到这个通道上. 为了演示,下面这个例子需要使用多个客户端. 发到客户端所订阅的通道上的消息包含三个元素:

  1. 消息的类型, 要么是 “消息” 要么是 “订阅” 要么是“取消订阅”
  2. 关联的通道.
  3. 消息本身.

SUBSCRIBE 开始监听消息的一个或多个通道:

> SUBSCRIBE whispers chatter

Reading messages... (press Ctrl-C to quit)

1) "subscribe"

2) "whispers"

3) (integer) 1

1) "subscribe"

2) "chatter"

3) (integer) 2

PUBLISH 发送一个消息到指定通道:

> PUBLISH whispers "hello ... world"

(integer) 1

(客户端收到的:)

1) "message"

2) "whispers"

3) "hello ... world”

Redis的队列

Redis 是一个几乎可在所有的应用中易于使用的轻量级数据存储工具. 在Redis特性的基础上可开发一些额外的功能. 一个很好的例子就是将Redis作为一个普通的队列来使用. 也有一些基于Redis的具备队列完全特性的队列方案(比如 Resque: http://resque.github.io/), 但一个简单的方法就是使用list数据类型来创建队列.

通过将元素添加到列表的左端,然后使用一个工作者(队列处理器)从列表的右端来消费元素,我们就创建了一个简单的队列. 我们使用 LPUSH将元素添加到队列:

> LPUSH todo breakfast

(integer) 1

> LPUSH todo newspaper

(integer) 2

> LPUSH todo email

(integer) 3

对于使用队列的组件,我们可以用 RPOP 从列表的右端获取元素. 但使用这个方法时要小心点; 当列表为空时,工作者一直重复尝试读取就会浪费一些资源在读取空的列表上! 相反, 我们可以使用阻塞的列表,使用 BRPOP来获取元素,用阻塞的命令意味着命令会在元素被添加到空列表前等待一段时间.

> BRPOP todo 1

1) "todo"

2) "breakfast"

> BRPOP todo 1

1) "todo"

2) "newspaper"

> BRPOP todo 1

1) "todo"

2) "email"

> BRPOP todo 1

(nil)

(1.03s)

基于Redis丰富的数据类型可以实现许多的不同特性.

Redis的事务

有时候,我们需要在Redis中将多个操作一起执行, 它们要么同时发生,要么什么也不做. Redis使用事务来支持这个特性 . 要注意的是如果其中的一个命令失败了,此时事务是不会回滚的; Redis里的事务更像队列一样,将所有的命令一次性的批处理执行.

MULTI开始收集作为事务处理命令的命令. DISCARD 撤消事务的命令 (我们根本不想执行它们). EXEC 执行所有的命令然后显示执行结果:

> SET a 1

OK

> MULTI

OK

> INCR a

QUEUED

> DISCARD

OK

> GET a

"1"

> MULTI

OK

> INCR a

QUEUED

> INCR a

QUEUED

> EXEC

1) (integer) 2

2) (integer) 3

>

本文分享自微信公众号 - IT技术精选文摘(ITHK01),作者:Lorna Mitchell

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-08-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Centos7部署Kubernetes集群

    yum安装的etcd默认配置文件在/etc/etcd/etcd.conf。编辑配置文件,更改以下带颜色部分信息:

    用户1263954
  • 面试题:你们有没有做 MySQL 读写分离?如何实现 MySQL 的读写分离?

    你们有没有做 MySQL 读写分离?如何实现 MySQL 的读写分离?MySQL 主从复制原理的是啥?如何解决 MySQL 主从同步的延时问题?

    用户1263954
  • Redis与Memcached的区别

    本文我们将探讨 Redis (远程字典服务器). Redis是一个开源的、内存型的键值存储。它也被看作为一个字典型的数据结构服务器,因为它的键值不仅仅是字符串,...

    用户1263954
  • 谷歌等巨头公布美政府机密数据请求具体次数

    北京时间2月4日上午消息,Facebook、微软、雅虎和谷歌周一开始公布美国政府机密数据请求的细节,希望借此证明它们对极富争议的大规模信息监控项...

    安恒信息
  • AngularJS in Action读书笔记4(实战篇)——创建Statistic模块

      个人感觉《Angularjs in action》这本书写的很好,很流畅,循序渐进,深入浅出,关键是结合了一个托管于Github上的实例讲解的,有代码可查,...

    JackieZheng
  • 三年研发、数亿美元成本,Mate 20的“大杀器”麒麟980是怎样炼成的?

    作为全球首款量产的7nm手机芯片、首款基于最新Cortex-A76 而开发的商用芯片、首款搭载Mali-G76 GPU架构的移动端芯片、双NPU加持……有人形容...

    量子位
  • 滴滴打车原理浅谈

    最近公司要做一款跟滴滴打车功能很类似的APP,就自己研究了一下滴滴打车的实现原理,纪录于笔。

    進无尽
  • Redis基础知识点快速复习手册(上)

    本文快速回顾了Redis书籍、博客以及本人面试中遇到的基础知识点,方便大家快速回顾知识。

    Rude3Knife的公众号
  • 编程思想 之「操作符」

    在 Java 编程的过程中,我们对数据的处理,都是通过操作符来实现的。例如,用于赋值的赋值操作符、用于运算的运算操作符、用于比较的比较操作符,还包括逻辑操作符、...

    CG国斌
  • Django---时间的时区问题

      在用django1.8版本做项目的时候遇到时间的存储与读取不一致的问题,网上找了很多帖子,但都没有讲明白。本文将在项目中遇到的问题及如何解决的尽可能详细的记...

    用户1214487

扫码关注云+社区

领取腾讯云代金券