Redis 基础知识
11月 5, 2019
Redis 介绍 #
历史 #
Redis 是一个使用 ANSI C 编写的开源、支持网络、基于内存、分布式、可选持久性的键值对存储数据库,由来自意大利西西里岛的 Salvatore Sanfilippo(Antirez)发起并编写。从2015年6月开始,Redis 的开发由 Redis Labs 赞助,而2013年5月至2015年6月期间,其开发由 Pivotal 赞助。在2013年5月之前,其开发由 VMware 赞助。根据月度排行网站 DB-Engines.com 的数据,Redis 是最流行的键值对存储数据库。
—— wiki 百科(这里我想到了吕布)
特性 #
- 自动过期
- 事务(multi 和 exec)
- 持久化
- 支持单机和多机使用
- 发布订阅
常用数据结构和命令 #
字符串(string) #
字符串可以是任意值,只要是字符串就行。
通过 set
和 get
命令使用:
#set
set key value
# examples
set number "10086"
set title "hello redis"
# get
get key
getset
获取旧值并获取新值。setnx
只在键不存在的情况下,将key
值设置为value
。setex
将 key 值设为value
,并将key
的生存时间设置为second
秒钟。
setex cached_key 60 "value"
# 等价于
set key cached_key value
expire cached_key 60
getrange
和setrange
对字符串的索引范围进行获取和操作。append
追加新内容到值的末尾。strlen
获取字符串的字节长度。
针对数字类型的值,Redis 提供了便于计算的命令:
incr
和decr
相当于+1 和 -1incrby
和decrby
可以指定加减的值
使用场景:
- 计数器
- ID 生成器
- 获取摘要
散列(hash) #
散列表结构中多了一个 “字段(field)” 的概念。
hset
和 hget
:
127.0.0.1:6379> hset m hello world
(integer) 1
127.0.0.1:6379> hlen m
(integer) 1
127.0.0.1:6379> hset m foo bar
(integer) 1
127.0.0.1:6379> hlen m
(integer) 2
127.0.0.1:6379> hgetall m
1) "hello"
2) "world"
3) "foo"
4) "bar"
127.0.0.1:6379> hget m hello
"world"
hexists
检查 key 是否存在。散列没有
hdecrby
这样的命令,如果要减的话需要在hincrby
传入负数。
另外在字符串中的一些命令,散列一般也有,不过命令一般会带一个 h
前缀。
数据库的键数量增多带来的问题主要和资源有关:
- 每个字符串的附加信息会占用内存。
- 散列在一定条件下Redis 可与进行内存优化。
- 更多的键会占用更多的 CPU 和内存,主要体现在持久化操作和模式匹配时。
如何选择使用字符串还是散列表:
- 如果要设置过期时间,那么使用字符串。
- 如果需要
setrange
和getrange
、append
的功能,散列不支持只能用字符串。 - 其他情况只要散列满足就尽量用散列表。
列表(list) #
lpush
和rpush
如果命令参数的 key 不存在,会新建一个,lpushx
和rpushx
不会新建。- 通过
llen
、lindex
、lrange
可以查看列表上的元素。 - 还可以通过
lset
、linsert
、lrem
修改和删除列表。
lpushx ll 0 1 2
lrange ll 0 3
# output
1) "2"
2) "1"
3) "0"
适用场景:
- 栈、队列
- 待办事项
集合(set) #
集合与列表相比,集合没有重复的元素,另外列表是以有序方式存储的。
sadd
、srem
添加和删除元素。smembers
获取集合包含的所有元素。sismember
检查元素是否在集合中。scard
获取集合包含的元素数量。
127.0.0.1:6379> sadd sss 0 1 2 3
(integer) 4
127.0.0.1:6379> spop sss
"0"
使用场景:
- 点赞
- 投票
- 社交关系
有序集合(zset) #
有序集合就是比集合多了个有序的特性,在功能上有一些类似列表的命令。zset 多了一个 score 的概念,叫分值,zset 这种数据结构每个元素都由一个成员和一个与成员相关联的分值组成。根据 score 形成了有序集合,并且其成员是独一无二的,分值可以是相同的。
zadd
、zrem
添加和删除元素。zcard
获取有序集合的大小。
其他常用命令 #
keys
命令接受一个全局匹配符作为参数,然后返回数据库中所有与这个匹配符相匹配的键作为结果。
同时 keys
命令需要检查数据库包含的所有键,并一次性将符合条件的所有键全部返回给客户端,所以当数据库包含的键数量比较大时,使用 keys 命令可能会导致服务器被阻塞。可以使用 scan
迭代数据库。
keys s*
# 使用 scan
scan 0 match s*
randomkey
:随机返回一个键,看起来没用实际也没啥用的一个命令。sort
:sort 命令可以对列表、集合元素和有序集合的成员进行排序。exists
:检查给定键是否存在。type
:查看数据类型。expire
:设置 key 的过期时间。
其他数据结构 #
其他的数据结构,复杂度都比较高,可以了解大致的用途后在需要的情况下专门按需学习。
位图 #
位图(bitmap)是由多个二进制位组成的数组,数组中的每个二进制位都有与之对应的偏移量(也称索引),用户通过这些偏移量可以对位图中指定的一个或多个二进制位进行操作。
地理坐标 #
就是地理坐标。
流 #
流是一个包含零个或任意多个流元素的有序队列,队列中的每个元素都包含一个ID和任意多个键值对,这些元素会根据ID的大小在流中有序地进行排列。
规范 #
命名 #
Redis 的 key 值一般以冒号连接,比如 user:name
。
持久化 #
Redis 提供了 RDB 持久化(默认开启)、AOF 持久化和 RDB-AOF 混合持久化等多种方式提供用户选择。
RDB #
save
命令:阻塞服务器并创建 RDB 文件。bgsave:非阻塞。
save
的参数可以指定时间在后台进行 bgsave,默认情况:
save 6010000
save 300100
save 3600 1
## 关闭持久化
save ""
RDB 的问题:
- 间隔越长,停机丢失的数据越多。
- 频繁创建 RDB 文件会对性能造成影响。
AOF #
AOF 提供的是增量式的持久化功能。核心原理是:服务器每次执行完写命令之后,都会以协议文本的方式将被执行的命令追加到AOF文件的末尾。这样一来,服务器在停机之后,只要重新执行AOF文件中保存的Redis命令,就可以将数据库恢复至停机之前的状态。
冲洗机制 #
通过 appendonly
配置打开或关闭 AOF。value options:yes/no
通过 appendfsync value
配置冲洗频率。value options:always/everysec/no
重写机制 #
重写机制会将冗余的命令操作合并。
小结 #
- AOF 持久化方案可以将数据丢失的时间窗口限制在 1s 以内。
- 因为持久化机制的原因,AOF 文件会比 RDB 文件要大。
混合持久化 #
将 aof-use-rdb-preamble
选项的值设置成 yes,开启RDB-AOF 混合持久化
数据库操作 #
配置 #
Redis 默认使用 6379 作为端口号,也可以自己在启动时自定义,同时可以指定配置文件。
redis-server --port 10086
redis-server /path/to/file
Redis 中默认有16个数据库,数据库是由一个整数索引标识(就是说数据库名是 0-15),而不是一个数据库名称。默认情况下,一个客户端连接到数据库 0。
切换数据库:
select 1
Redis 配置文件中下面的参数来控制数据库总数:/etc/redis/redis.conf 文件中,有个配置项 databases = 16。
多机方案 #
Redis 除了支持单机部署,也支持多机部署,常见的多机部署为主从、哨兵。
(1)Replica 主从:replicaof 命令手动操作进行故障转移。
(2)Sentinel 哨兵模式:自动故障转移。
(3)Cluster 集群方案:引入节点概念,分为主节点(master node)和从节点(slave/replica node)。
发布订阅机制 #
在 Redis 中,客户端(client)可以通过订阅特定的频道(channel)来接收发送至该频道的消息(message),我们把这些订阅频道的客户端称为订阅者(subscriber),发送消息的客服端称为(publisher)。
除了订阅频道之外,客户端还可以通过订阅模式(pattern)来接收消息:每当发布者向某个频道发送消息的时候,不仅频道的订阅者会收到消息,与频道匹配的所有模式的订阅者也会收到消息。即既可以订阅频道,也可以订阅模式。
常见问题 #
缓存击穿 #
是指在查询一个缓存中一定不存在的数据,出于容错考虑,一般会从数据库层继续查询,如果数据库不存在一般不会进行缓存,这样在数据量大时,很可能导致数据库挂掉。
解决方案:
- Bloom Filter,布隆过滤器可以判断一个数据在数据库中一定不存在,这样可以避免大量恶意请求打到数据库。
- 查询的结果为空时同样进行缓存。
缓存雪崩 #
是指由于一些原因我们在缓存中的数据几乎同时过期,此时请求会全部打到数据库,数据库的压力就会瞬间过载。
还有一种情况是并发太高导致缓存服务崩溃,从而导致请求都到数据库。
解决方案:
- 将缓存的过期时间分散开,可以增加一个随机值。
- 采用高可用方案:主从/哨兵/集群。
- 限流、服务降级。
- 增加持久化方案,在缓存服务器重启后快速恢复数据。