如需转载,请根据 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 许可,附上本文作者及链接。
本文作者: 执笔成念
作者昵称: zbcn
本文链接: https://1363653611.github.io/zbcn.github.io/2020/01/11/redis_02%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98/
使用 redis 有哪些好处?
- 速度快,因为数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是 O(1)
- 支持丰富数据类型,支持 string,list,set,sorted set,hash
- 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
- 丰富的特性:可用于缓存,消息,按 key 设置过期时间,过期后将会自动删除
redis 相比 memcached 有哪些优势?
- memcached 所有的值均是简单的字符串,redis 作为其替代者,支持更为丰富的数据类型
- redis 的速度比 memcached 快很多
- redis 可以持久化其数据
redis 的操作为什么是原子性的?
- Redis是单线程的。
- 线程,是操作系统最小的执行单元,在单线程程序中,任务一个一个地做,必须做完一个任务后,才会去做另一个任务。
Redis在并发中的表现
- 对Redis来说,执行get、set以及eval等API,都是一个一个的任务,这些任务都会由Redis的线程去负责执行,任务要么执行成功,要么执行失败,这就是Redis的命令是原子性的原因。
- Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。
Redis为什么这么快
- 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
- 数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
- 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
- 使用多路I/O复用模型,非阻塞IO;
- 使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
Memcache 与 Redis 的区别都有哪些?
- 存储方式 Memecache 把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis 有部份存在硬盘上,这样能保证数据的持久性
- 数据支持类型 Memcache 对数据类型支持相对简单。 Redis 有复杂的数据类型。
- 使用底层模型不同 它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。 Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
redis 常见性能问题和解决方案:
- Master 写内存快照,save 命令调度 rdbSave 函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以 Master 最好不要写内存快照。
- Master AOF 持久化,如果不重写 AOF 文件,这个持久化方式对性能的影响是最小的,但是 AOF 文件会不断增大,AOF 文件过大会影响 Master 重启的恢复速度。Master 最好不要做任何持久化工作,包括内存快照和 AOF 日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个 Slave 开启 AOF 备份数据,策略为每秒同步一次。
- .Master 调用 BGREWRITEAOF 重写 AOF 文件,AOF 在重写的时候会占大量的 CPU 和内存资源,导致服务 load 过高,出现短暂服务暂停现象
- Redis 主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave 和 Master 最好在同一个局域网内
- mySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证 redis 中的数据都是热点数据
- redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略(回收策略)。redis 提供 6 种数据淘汰策略:
- volatile-lru:从已设置过期时间的数据集(server.db [i].expires)中挑选 最近最少使用 的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db [i].expires)中挑选 将要过期 的数据淘汰
- olatile-random:从已设置过期时间的数据集(server.db [i].expires)中 任意选择数据 淘汰
- allkeys-lru:从数据集(server.db [i].dict)中挑选 最近最少使用 的数据淘汰
- allkeys-random:从数据集(server.db [i].dict)中 任意选择数据 淘汰
- no-enviction(驱逐):禁止驱逐数据
- redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略(回收策略)。redis 提供 6 种数据淘汰策略:
为什么 redis 需要把所有数据放到内存中?
Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以 redis 具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘 I/O 速度为严重影响 redis 的性能。在内存越来越便宜的今天,redis 将会越来越受欢迎。
如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。
redis 的并发竞争问题如何解决?
Redis 为单进程单线程模式,采用队列模式将并发访问变为串行访问.
Redis 本身没有锁的概念,Redis 对于多个客户端连接并不存在竞争,但是在 Jedis 客户端对 Redis 进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是由于客户端连接混乱造成。对此有 2 种解决方法:
- 客户端角度,为保证每个客户端间正常有序与 Redis 进行通信,对连接进行池化,同时对客户端读写 Redis 操作采用内部锁 synchronized
服务器角度,利用 setnx 实现锁。
注意:
- 对于第一种,需要应用程序自己处理资源的同步,可以使用的方法比较通俗,可以使用 synchronized 也可以使用 lock
- 第二种需要用到 Redis 的 setnx 命令,但是需要注意一些问题
redis 事物的了解 CAS (check-and-set 操作实现乐观锁)?
- 和众多其它数据库一样,Redis 作为 NoSQL 数据库也同样提供了事务机制。在 Redis 中,MULTI/EXEC/DISCARD/WATCH 这四个命令是我们实现事务的基石。
- Redis 中事务的实现特征:
1. 在事务中的所有命令都将会被串行化的顺序执行,事务执行期间,Redis 不会再为其它客户端的请求提供任何服务,从而保证了事物中的所有命令被原子的执行。 2. 和关系型数据库中的事务相比,在 Redis 事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。 3. 我们可以通过 MULTI 命令开启一个事务,有关系型数据库开发经验的人可以将其理解为 "BEGIN TRANSACTION" 语句。在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过执行 EXEC/DISCARD 命令来提交 / 回滚该事务内的所有操作。这两个 Redis 命令可被视为等同于关系型数据库中的 COMMIT/ROLLBACK 语句。 4. 在事务开启之前,如果客户端与服务器之间出现通讯故障并导致网络断开,其后所有待执行的语句都将不会被服务器执行。然而如果网络中断事件是发生在客户端执行 EXEC 命令之后,那么该事务中的所有命令都会被服务器执行 5. 当使用 Append-Only 模式时,Redis 会通过调用系统函数 write 将该事务内的所有写操作在本次调用中全部写入磁盘。然而如果在写入的过程中出现系统崩溃,如电源故障导致的宕机,那么此时也许只有部分数据被写入到磁盘,而另外一部分数据却已经丢失。 6. Redis 服务器会在重新启动时执行一系列必要的一致性检测,一旦发现类似问题,就会立即退出并给出相应的错误提示。此时,我们就要充分利用 Redis 工具包中提供的 redis-check-aof 工具,该工具可以帮助我们定位到数据不一致的错误,并将已经写入的部分数据进行回滚。修复之后我们就可以再次重新启动 Redis 服务器了。
WATCH 命令和基于 CAS 的乐观锁:
在 Redis 的事务中,WATCH 命令可用于提供 CAS (check-and-set) 功能。假设我们通过 WATCH 命令在事务执行之前监控了多个 Keys,倘若在 WATCH 之后有任何 Key 的值发生了变化,EXEC 命令执行的事务都将被放弃,同时返回 Null multi-bulk 应答以通知调用者事务执行失败。
- eg:我们再次假设 Redis 中并未提供 incr 命令来完成键值的原子性递增,如果要实现该功能,我们只能自行编写相应的代码。其伪码如下:以上代码只有在单连接的情况下才可以保证执行结果是正确的,因为如果在同一时刻有多个客户端在同时执行该段代码,那么就会出现多线程程序中经常出现的一种错误场景 – 竞态争用 (race condition).
1
val = GET mykey
2
val = val + 1
3
SET mykey $val
分析问题:客户端 A 和 B 都在同一时刻读取了 mykey 的原有值,假设该值为 10,此后两个客户端又均将该值加一后 set 回 Redis 服务器,这样就会导致 mykey 的结果为 11,而不是我们认为的 12。
为了解决类似的问题,我们需要借助 WATCH 命令的帮助,见如下代码:
1
WATCH mykey
2
val = GET mykey
3
val = val + 1
4
MULTI
5
SET mykey $val
6
EXEC
和此前代码不同的是,新代码在获取 mykey 的值之前先通过 WATCH 命令监控了该键,此后又将 set 命令包围在事务中,这样就可以有效的保证每个连接在执行 EXEC 之前,如果当前连接获取的 mykey 的值被其它连接的客户端修改,那么当前连接的 EXEC 命令将执行失败。这样调用者在判断返回值后就可以获悉 val 是否被重新设置成功
- eg:我们再次假设 Redis 中并未提供 incr 命令来完成键值的原子性递增,如果要实现该功能,我们只能自行编写相应的代码。其伪码如下:
redis 持久化的几种方式
快照(snapshots)(rdb)
缺省情况情况下,Redis 把数据快照存放在磁盘上的二进制文件中,文件名为 dump.rdb。你可以配置 Redis 的持久化策略,例如数据集中每 N 秒钟有超过 M 次更新,就将数据写入磁盘;或者你可以手工调用命令 SAVE 或 BGSAVE。
- 工作原理
1. Redis forks.
2. 子进程开始将数据写到临时 RDB 文件中。
3. 当子进程完成写 RDB 文件,用新文件替换老文件。
4. 这种方式可以使 Redis 使用 copy-on-write 技术
AOF
快照模式并不十分健壮,当系统停止,或者无意中 Redis 被 kill 掉,最后写入 Redis 的数据就会丢失。这对某些应用也许不是大问题,但对于要求高可靠性的应用来说;Redis 就不是一个合适的选择。Append-only 文件模式是另一种选择。你可以在配置文件中打开 AOF 模式
虚拟内存方式
- 当你的 key 很小而 value 很大时,使用 VM 的效果会比较好。因为这样节约的内存比较大.
- 当你的 key 不小时,可以考虑使用一些非常方法将很大的 key 变成很大的 value, 比如你可以考虑将 key,value 组合成一个新的 value.
- vm-max-threads 这个参数,可以设置访问 swap 文件的线程数,设置最好不要超过机器的核数,如果设置为 0, 那么所有对 swap 文件的操作都是串行的。可能会造成比较长时间的延迟,但是对数据完整性有很好的保证.
- 自己测试的时候发现用虚拟内存性能也不错。如果数据量很大,可以考虑分布式或者其他数据库
什么是 AOF(Append Only File)/RDB(Redis DataBase) 持久化?
RDB 将数据库的快照(snapshot)以二进制的方式保存到磁盘中。
AOF 则以协议文本的方式,将所有对数据库进行过写入的命令(及其参数)记录到 AOF 文件,以此达到记录数据库状态的目的。
同步命令到 AOF 文件的整个过程可以分为三个阶段:
- 命令传播:Redis 将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF 程序中。
- 缓存追加:AOF 程序根据接收到的命令数据,将命令转换为网络通讯协议的格式,然后将协议内容追加到服务器的 AOF 缓存中。
- 文件写入和保存:AOF 缓存中的内容被写入到 AOF 文件末尾,如果设定的 AOF 保存条件被满足的话, fsync 函数或者 fdatasync 函数会被调用,将写入的内容真正地保存到磁盘中。
在 Redis 运行时, RDB 程序将当前内存中的数据库快照保存到磁盘文件中, 在 Redis 重启动时, RDB 程序可以通过载入 RDB 文件来还原数据库的状态。
RDB 功能最核心的是 rdbSave 和 rdbLoad 两个函数, 前者用于生成 RDB 文件到磁盘, 而后者则用于将 RDB 文件中的数据重新载入到内存中
RDB 和 AOF 的区别
- RDB,简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上;
- AOF,则是换了一个角度来实现持久化,那就是将redis执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。
- 其实RDB和AOF两种方式也可以同时使用,在这种情况下,如果redis重启的话,则会优先采用AOF方式来进行数据恢复,这是因为AOF方式的数据恢复完整度更高。
- 、如果你没有数据持久化的需求,也完全可以关闭RDB和AOF方式,这样的话,redis将变成一个纯内存数据库,就像memcache一样。
redis持久化—-RDB
- RDB方式,是将redis某一时刻的数据持久化到磁盘中,是一种快照式的持久化方法。
- redis在进行数据持久化的过程中,会先将数据写入到一个临时文件中,待持久化过程都结束了,才会用这个临时文件替换上次持久化好的文件。正是这种特性,让我们可以随时来进行备份,因为快照文件总是完整可用的。
- 对于RDB方式,redis会单独创建(fork)一个子进程来进行持久化,而主进程是不会进行任何IO操作的,这样就确保了redis极高的性能。
- 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
- 虽然RDB有不少优点,但它的缺点也是不容忽视的。如果你对数据的完整性非常敏感,那么RDB方式就不太适合你,因为即使你每5分钟都持久化一次,当redis故障时,仍然会有近5分钟的数据丢失。所以,redis还提供了另一种持久化方式,那就是AOF。
redis持久化—-AOF
- AOF,英文是Append Only File,即只允许追加不允许改写的文件。
- 如前面介绍的,AOF方式是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行一遍,就这么简单。
- 我们通过配置redis.conf中的appendonly yes就可以打开AOF功能。如果有写操作(如SET等),redis就会被追加到AOF文件的末尾。
- 默认的AOF持久化策略是每秒钟fsync一次(fsync是指把缓存中的写指令记录到磁盘中),因为在这种情况下,redis仍然可以保持很好的处理性能,即使redis故障,也只会丢失最近1秒钟的数据。
- 如果在追加日志时,恰好遇到磁盘空间满、inode满或断电等情况导致日志写入不完整,也没有关系,redis提供了redis-check-aof工具,可以用来进行日志修复。
- 因为采用了追加方式,如果不做任何处理的话,AOF文件会变得越来越大,为此,redis提供了AOF文件重写(rewrite)机制,即当AOF文件的大小超过所设定的阈值时,redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。举个例子或许更形象,假如我们调用了100次INCR指令,在AOF文件中就要存储100条指令,但这明显是很低效的,完全可以把这100条指令合并成一条SET指令,这就是重写机制的原理。
- 在进行AOF重写时,仍然是采用先写临时文件,全部完成后再替换的流程,所以断电、磁盘满等问题都不会影响AOF文件的可用性,这点大家可以放心。
- AOF方式的另一个好处,我们通过一个“场景再现”来说明。某同学在操作redis时,不小心执行了FLUSHALL,导致redis内存中的数据全部被清空了,这是很悲剧的事情。不过这也不是世界末日,只要redis配置了AOF持久化方式,且AOF文件还没有被重写(rewrite),我们就可以用最快的速度暂停redis并编辑AOF文件,将最后一行的FLUSHALL命令删除,然后重启redis,就可以恢复redis的所有数据到FLUSHALL之前的状态了。是不是很神奇,这就是AOF持久化方式的好处之一。但是如果AOF文件已经被重写了,那就无法通过这种方法来恢复数据了。
- 虽然优点多多,但AOF方式也同样存在缺陷,比如在同样数据规模的情况下,AOF文件要比RDB文件的体积大。而且,AOF方式的恢复速度也要慢于RDB方式。
- 如果你直接执行BGREWRITEAOF命令,那么redis会生成一个全新的AOF文件,其中便包括了可以恢复现有数据的最少的命令集。
- 如果运气比较差,AOF文件出现了被写坏的情况,也不必过分担忧,redis并不会贸然加载这个有问题的AOF文件,而是报错退出。这时可以通过以下步骤来修复出错的文件:
- 在重写即将开始之际,redis会创建(fork)一个“重写子进程”,这个子进程会首先读取现有的AOF文件,并将其包含的指令进行分析压缩并写入到一个临时文件中。
- 与此同时,主工作进程会将新接收到的写指令一边累积到内存缓冲区中,一边继续写入到原有的AOF文件中,这样做是保证原有的AOF文件的可用性,避免在重写过程中出现意外。
- 当“重写子进程”完成重写工作后,它会给父进程发一个信号,父进程收到信号后就会将内存中缓存的写指令追加到新AOF文件中。
- 当追加结束后,redis就会用新AOF文件来代替旧AOF文件,之后再有新的写指令,就都会追加到新的AOF文件中了。
redis持久化—-如何选择RDB和AOF
- 对于我们应该选择RDB还是AOF,官方的建议是两个同时使用。这样可以提供更可靠的持久化方案。
- redis的备份和还原,可以借助第三方的工具redis-dump。
Redis的两种持久化方式也有明显的缺点
- RDB需要定时持久化,风险是可能会丢两次持久之间的数据,量可能很大。
- AOF每秒fsync一次指令硬盘,如果硬盘IO慢,会阻塞父进程;风险是会丢失1秒多的数据;在Rewrite过程中,主进程把指令存到mem-buffer中,最后写盘时会阻塞主进程。
小结
- AOF 文件通过保存所有修改数据库的命令来记录数据库的状态。
- AOF 文件中的所有命令都以 Redis 通讯协议的格式保存。
- 不同的 AOF 保存模式对数据的安全性、以及 Redis 的性能有很大的影响。
- AOF 重写的目的是用更小的体积来保存数据库状态,整个重写过程基本上不影响 Redis 主进程处理命令请求。
- AOF 重写是一个有歧义的名字,实际的重写工作是针对数据库的当前值来进行的,程序既不读写、也不使用原有的 AOF 文件。
- AOF 可以由用户手动触发,也可以由服务器自动触发。
redis 的缓存失效策略和主键失效机制
- 作为缓存系统都要定期清理无效数据,就需要一个主键失效和淘汰策略.
- 在 Redis 当中,有生存期的 key 被称为 volatile。在创建缓存时,要为给定的 key 设置生存期,当 key 过期的时候(生存期为 0),它可能会被删除。
1、影响生存时间的一些操作
- 生存时间可以通过使用 DEL 命令来删除整个 key 来移除,或者被 SET 和 GETSET 命令覆盖原来的数据,也就是说,修改 key 对应的 value 和使用另外相同的 key 和 value 来覆盖以后,当前数据的生存时间不同。
- 比如说,对一个 key 执行 INCR 命令,对一个列表进行 LPUSH 命令,或者对一个哈希表执行 HSET 命令,这类操作都不会修改 key 本身的生存时间。另一方面,如果使用 RENAME 对一个 key 进行改名,那么改名后的 key 的生存时间和改名前一样。
- RENAME 命令的另一种可能是,尝试将一个带生存时间的 key 改名成另一个带生存时间的 another_key ,这时旧的 another_key (以及它的生存时间) 会被删除,然后旧的 key 会改名为 another_key ,因此,新的 another_key 的生存时间也和原本的 key 一样。使用 PERSIST 命令可以在不删除 key 的情况下,移除 key 的生存时间,让 key 重新成为一个 persistent key 。
如何更新生存时间
- 可以对一个已经带有生存时间的 key 执行 EXPIRE 命令,新指定的生存时间会取代旧的生存时间。过期时间的精度已经被控制在 1ms 之内,主键失效的时间复杂度是 O(1),
- EXPIRE 和 TTL 命令搭配使用,TTL 可以查看 key 的当前生存时间。设置成功返回 1;当 key 不存在或者不能为 key 设置生存时间时,返回 0 。
最大缓存配置
在 redis 中,允许用户设置最大使用内存大小
server.maxmemory
默认为 0,没有指定最大缓存,如果有新的数据添加,超过最大内存,则会使 redis 崩溃,所以一定要设置。redis 内存数据集大小上升到一定大小的时候,就会实行数据淘汰策略。
redis 提供 6 种数据淘汰策略:
- volatile-lru:从已设置过期时间的数据集(server.db [i].expires)中挑选最近最少使用的数据淘汰
2. volatile-ttl:从已设置过期时间的数据集(server.db [i].expires)中挑选将要过期的数据淘汰
3. volatile-random:从已设置过期时间的数据集(server.db [i].expires)中任意选择数据淘汰
4. allkeys-lru:从数据集(server.db [i].dict)中挑选最近最少使用的数据淘汰
5. allkeys-random:从数据集(server.db [i].dict)中任意选择数据淘汰
6. no-enviction(驱逐):禁止驱逐数据注意这里的 6 种机制,volatile 和 allkeys 规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据,后面的 lru、ttl 以及 random 是三种不同的淘汰策略,再加上一种 no-enviction 永不回收的策略。
- volatile-lru:从已设置过期时间的数据集(server.db [i].expires)中挑选最近最少使用的数据淘汰
使用策略规则:
- 如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用 allkeys-lru
- 如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用 allkeys-random
三种数据淘汰策略:
ttl 和 random 比较容易理解,实现也会比较简单。主要是 Lru 最近最少使用淘汰策略,设计上会对 key 按失效时间排序,然后取最先失效的 key 进行淘汰
redis 最适合的场景
Redis 最适合所有数据 in-momory 的场景,虽然 Redis 也提供持久化功能,但实际更多的是一个 disk-backed 的功能,跟传统意义上的持久化有比较大的差别,那么可能大家就会有疑问,似乎 Redis 更像一个加强版的 Memcached,那么何时使用 Memcached, 何时使用 Redis 呢?
- 如果简单地比较 Redis 与 Memcached 的区别,大多数都会得到以下观点:
会话缓存(Session Cache)
最常用的一种使用 Redis 的情景是会话缓存(session cache)。用 Redis 缓存会话比其他存储(如 Memcached)的优势在于:Redis 提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用 Redis 来缓存会话的文档。甚至广为人知的商业平台 Magento 也提供 Redis 的插件。
全页缓存(FPC)
除基本的会话 token 之外,Redis 还提供很简便的 FPC 平台。回到一致性问题,即使重启了 Redis 实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似 PHP 本地 FPC。
再次以 Magento 为例,Magento 提供一个插件来使用 Redis 作为全页缓存后端。
此外,对 WordPress 的用户来说,Pantheon 有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。
- 队列
Reids 在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得 Redis 能作为一个很好的消息队列平台来使用。Redis 作为队列使用的操作,就类似于本地程序语言(如 Python)对 list 的 push/pop 操作。
如果你快速的在 Google 中搜索 “Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用 Redis 创建非常好的后端工具,以满足各种队列需求。例如,Celery 有一个后台就是使用 Redis 作为 broker,你可以从这里去查看。
- 排行榜 / 计数器
Redis 在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis 只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的 10 个用户–我们称之为 “user_scores”,我们只需要像下面一样执行即可:
当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
ZRANGE user_scores 0 10 WITHSCORES
Agora Games 就是一个很好的例子,用 Ruby 实现的,它的排行榜就是使用 Redis 来存储数据的,你可以在这里看到。 - 发布 / 订阅
最后(但肯定不是最不重要的)是 Redis 的发布 / 订阅功能。发布 / 订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布 / 订阅的脚本触发器,甚至用 Redis 的发布 / 订阅功能来建立聊天系统!(不,这是真的,你可以去核实)。
Redis 提供的所有特性中,我感觉这个是喜欢的人最少的一个,虽然它为用户提供如果此多功能。
进程与线程的区别
- 资源(如打开文件):进程间的资源相互独立,同一进程的各线程间共享资源。某进程的线程在其他进程不可见。
- 通信:
- 进程间通信:消息传递、同步、共享内存、远程过程调用、管道。
- 线程间通信:直接读写进程数据段(需要进程同步和互斥手段的辅助,以保证数据的一致性)。
- 调度和切换:线程上下文切换比进程上下文切换要快得多。
- 线程,是操作系统最小的执行单元,在单线程程序中,任务一个一个地做,必须做完一个任务后,才会去做另一个任务。
多路 I/O 复用模型
多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。
这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量。