基础概念
Redis 是一个基于内存的高性能 Key-Value 数据库。
Redis 适合的场景主要局限在较小数据量的高性能操作和运算上。
优点
- 因为是纯内存操作,Redis 的性能非常出色,每秒可以处理超过 10 万次读写操作
- 支持保存多种数据结构,此外单个 value 的最大限制是 1GB,memcached 只能保存 1MB 的数据
- 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
- 可以选择持久化数据
缺点
- 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写
常见性能问题和解决方案
- Master 写内存快照,save 命令调度 rdbSave 函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以 Master 最好不要写内存快照。
- Master AOF 持久化,如果不重写 AOF 文件,这个持久化方式对性能的影响是最小的,但是 AOF 文件会不断增大,AOF 文件过大会影响 Master 重启的恢复速度。Master 最好不要做任何持久化工作,包括内存快照和 AOF 日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个 Slave 开启 AOF 备份数据,策略为每秒同步一次。
- Master 调用 BGREWRITEAOF 重写 AOF 文件,AOF 在重写的时候会占大量的CPU和内存资源,导致服务 load 过高,出现短暂服务暂停现象。
- Redis 主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave 和 Master 最好在同一个局域网内。
数据淘汰(回收)策略
该策略存在的意义是保证 Redis 中均为热点数据,Redis 共提供 6 种策略:
- volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- no-enviction(驱逐):禁止驱逐数据
线程和并发
- Redis 是单进程单线程的
- 利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销。
- 本身没有锁的概念,对于多个客户端连接不存在竞争
- 部分客户端对 Redis 进行并发访问时发生的连接超时、数据转换错误、阻塞、客户端连接关闭等问题,这些都是客户端的问题
如何解决并发竞争的问题
- 客户端角度,为保证每个客户端间正常有序与 Redis 进行通信,对连接进行池化,同时对客户端读写 Redis 操作采用内部锁
synchronized
- 服务器角度,利用
setnx
实现锁
对于第一种,需要应用程序自己处理资源的同步,可以使用的方法比较通俗,可以使用 synchronized
也可以使用 lock
。第二种需要用到 Redis 的 setnx
命令,但是需要注意一些问题。
事务 CAS check-and-set 操作实现乐观锁
Redis 作为 NoSQL 数据库也同样提供了事务机制。在 Redis 中,MULTI/EXEC/DISCARD/WATCH
这四个命令是我们实现事务的基石。
简要的列出 Redis 中事务的实现特征:
- 在事务中的所有命令都将会被串行化的顺序执行,事务执行期间,Redis 不会再为其它客户端的请求提供任何服务,从而保证了事物中的所有命令被原子的执行。
- 和关系型数据库中的事务相比,在 Redis 事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。
- 我们可以通过
MULTI
命令开启一个事务,有关系型数据库开发经验的人可以将其理解为 BEGIN TRANSACTION
语句。在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过执行EXEC/DISCARD
命令来提交/回滚该事务内的所有操作。这两个 Redis 命令可被视为等同于关系型数据库中的 COMMIT/ROLLBACK
语句。
- 在事务开启之前,如果客户端与服务器之间出现通讯故障并导致网络断开,其后所有待执行的语句都将不会被服务器执行。然而如果网络中断事件是发生在客户端执行
EXEC
命令之后,那么该事务中的所有命令都会被服务器执行。
- 当使用 Append-Only 模式时,Redis 会通过调用系统函数 write 将该事务内的所有写操作在本次调用中全部写入磁盘。然而如果在写入的过程中出现系统崩溃,如电源故障导致的宕机,那么此时也许只有部分数据被写入到磁盘,而另外一部分数据却已经丢失。
Redis 服务器会在重新启动时执行一系列必要的一致性检测,一旦发现类似问题,就会立即退出并给出相应的错误提示。
此时,我们就要充分利用 Redis 工具包中提供的 redis-check-aof 工具,该工具可以帮助我们定位到数据不一致的错误,并将已经写入的部分数据进行回滚。修复之后我们就可以再次重新启动 Redis 服务器了。
WATCH 命令和基于 CAS 的乐观锁
在 Redis 的事务中,WATCH
命令可用于提供 CAS(check-and-set) 功能。假设我们通过 WATCH
命令在事务执行之前监控了多个 Keys,倘若在 WATCH
之后有任何 Key 的值发生了变化,EXEC
命令执行的事务都将被放弃,同时返回 Null multi-bulk
应答以通知调用者事务。
执行失败。例如,我们再次假设 Redis 中并未提供 incr
命令来完成键值的原子性递增,如果要实现该功能,我们只能自行编写相应的代码。
适宜的场景
Redis 最适合所有数据 in-momory 的场景,虽然 Redis 也提供持久化功能,但实际更多的是一个 disk-backed 的功能,跟传统意义上的持久化有比较大的差别。
- 会话缓存 Session Cache,Redis 提供持久化,用其来缓存购物车和 Token 之类的信息就很合适了
- 全页缓存 FPC
- 队列,Redis queues
- 排行榜/计数器
- 发布订阅
原文