1.摘要
Redis(REmote DIctionary Server) 是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
对一个产品,技术的认知,从基本的安装和使用开始最容易获得直观认知。
本文包括REDIS在UBUNTU的安装,基本命令的操作使用和不错的参考文档。
具体目录结构如下:
- 安装
- 命令
3.1 REDIS配置
3.2 设置密码和验证
3.3 REDIS支持的类型
<1> String(字符串)
<2>Hash(哈希)
<3> List(列表)
<4> Set(集合)
<5> zset(sorted set:有序集合)
3.4 Redis 发布订阅 - REDIS常见问题
5.Redis面试题
6.参考
2. 安装
在 Ubuntu 系统安装 Redis 可以使用以下命令:
sudo apt-get update
sudo apt-get install redis-server
启动 Redis:
redis-server
查看 redis 是否启动:
redis-cli
以上命令将打开以下终端:
redis 127.0.0.1:6379>
127.0.0.1 是本机 IP ,6379 是 redis 服务端口。现在我们输入 PING 命令,能返回PONG表示成功了。
3. 命令
3.1 REDIS配置
Redis 的配置文件位于 Redis 安装目录下(ubuntu系统下,默认为/etc/redis/redis.conf),文件名为 redis.conf(Windows 名为 redis.windows.conf)。
你可以通过 CONFIG 命令查看或设置配置项。
redis 127.0.0.1:6379> CONFIG GET *
redis.conf 配置项说明如下:
序号 | 配置项 | 说明 |
---|---|---|
1 | daemonize no Redis | 默认不是以守护进程的方式运行,可以通过该配置项修改,使用 yes 启用守护进程(Windows 不支持守护线程的配置为 no ) |
2 | pidfile /var/run/redis.pid | 当 Redis 以守护进程方式运行时,Redis 默认会把 pid 写入 /var/run/redis.pid 文件,可以通过 pidfile 指定 |
3 | port 6379 | 指定 Redis 监听端口,默认端口为 6379,作者在自己的一篇博文中解释了为什么选用 6379 作为默认端口,因为 6379 在手机按键上 MERZ 对应的号码,而 MERZ 取自意大利歌女 Alessia Merz 的名字 |
4 | bind 127.0.0.1 | 绑定的主机地址 |
5 | timeout 300 | 当客户端闲置多长时间后关闭连接,如果指定为 0,表示关闭该功能 |
6 | loglevel notice | 指定日志记录级别,Redis 总共支持四个级别:debug、verbose、notice、warning,默认为 notice |
7 | logfile stdout | 日志记录方式,默认为标准输出,如果配置 Redis 为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给 /dev/null |
8 | databases 16 | 设置数据库的数量,默认数据库为0,可以使用SELECT 命令在连接上指定数据库id |
9 | save <seconds> <changes> | Redis 默认配置文件中提供了三个条件:save 900 1 save 300 10 save 60 10000 分别表示 900 秒(15 分钟)内有 1 个更改,300 秒(5 分钟)内有 10 个更改以及 60 秒内有 10000 个更改。指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合 |
10 | rdbcompression yes | 指定存储至本地数据库时是否压缩数据,默认为 yes,Redis 采用 LZF 压缩,如果为了节省 CPU 时间,可以关闭该选项,但会导致数据库文件变的巨大 |
11 | dbfilename dump.rdb | 指定本地数据库文件名,默认值为 dump.rdb |
12 | dir ./ | 指定本地数据库存放目录 |
13 | slaveof <masterip> <masterport> | 设置当本机为 slav 服务时,设置 master 服务的 IP 地址及端口,在 Redis 启动时,它会自动从 master 进行数据同步 |
14 | masterauth <master-password> | 当 master 服务设置了密码保护时,slav 服务连接 master 的密码 |
15 | requirepass foobared | 设置 Redis 连接密码,如果配置了连接密码,客户端在连接 Redis 时需要通过 AUTH <password> 命令提供密码,默认关闭 |
16 | maxclients 128 | 设置同一时间最大客户端连接数,默认无限制,Redis 可以同时打开的客户端连接数为 Redis 进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回 max number of clients reached 错误信息 |
17 | maxmemory <bytes> | 指定 Redis 最大内存限制,Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 会先尝试清除已到期或即将到期的 Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis 新的 vm 机制,会把 Key 存放内存,Value 会存放在 swap 区 |
18 | appendonly no | 指定是否在每次更新操作后进行日志记录,Redis 在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis 本身同步数据文件是按上面 save 条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为 no |
19 | appendfilename appendonly.aof | 指定更新日志文件名,默认为 appendonly.aof |
20 | appendfsync everysec | 指定更新日志条件,共有 3 个可选值:no:表示等操作系统进行数据缓存同步到磁盘(快)always:表示每次更新操作后手动调用 fsync() 将数据写到磁盘(慢,安全)everysec:表示秒同步一次(折中,默认值) |
21 | vm-enabled no | 指定是否启用虚拟内存机制,默认值为 no,简单的介绍一下,VM 机制将数据分页存放,由 Redis 将访问量较少的页即冷数据 swap 到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析 Redis 的 VM 机制) |
22 | vm-swap-file /tmp/redis.swap | 虚拟内存文件路径,默认值为 /tmp/redis.swap,不可多个 Redis 实例共享 |
23 | vm-max-memory 0 | 将所有大于 vm-max-memory 的数据存入虚拟内存,无论 vm-max-memory 设置多小,所有索引数据都是内存存储的(Redis 的索引数据 就是 keys),也就是说,当 vm-max-memory 设置为 0 的时候,其实是所有 value 都存在于磁盘。默认值为 0 |
24 | vm-page-size 32 | Redis swap 文件分成了很多的 page,一个对象可以保存在多个 page 上面,但一个 page 上不能被多个对象共享,vm-page-size 是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page 大小最好设置为 32 或者 64bytes;如果存储很大大对象,则可以使用更大的 page,如果不确定,就使用默认值 |
25 | vm-pages 134217728 | 设置 swap 文件中的 page 数量,由于页表(一种表示页面空闲或使用的 bitmap)是在放在内存中的,,在磁盘上每 8 个 pages 将消耗 1byte 的内存。 |
26 | vm-max-threads 4 | 设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4 |
27 | glueoutputbuf yes | 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启 |
28 | hash-max-zipmap-entries 64 | hash-max-zipmap-value 512 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法 |
29 | activerehashing yes | 指定是否激活重置哈希,默认为开启(后面在介绍 Redis 的哈希算法时具体介绍) |
30 | include /path/to/local.conf | 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件 |
更多信息可参考:https://www.runoob.com/redis/redis-conf.html
3.2 设置密码和验证
在配置文件中配置requirepass的密码(当redis重启时密码依然有效)。
redis 127.0.0.1:6379> config set requirepass mypass
查询密码:
redis 127.0.0.1:6379> config get requirepass
(error) ERR operation not permitted
密码验证:
redis 127.0.0.1:6379> auth mypass
OK
再次查询:
redis 127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "mypass"
PS:如果配置文件中没添加密码 那么redis重启后,密码失效;
如果需要在远程 redis 服务上执行命令,同样我们使用的也是 redis-cli 命令。
$ redis-cli -h host -p port -a password
【实例】
以下实例演示了如何连接到主机为 127.0.0.1,端口为 6379 ,密码为 mypass 的 redis 服务上。
$redis-cli -h 127.0.0.1 -p 6379 -a "mypass"
redis 127.0.0.1:6379>
先登陆后验证:
redis 127.0.0.1:6379> auth "mypass"
OK
AUTH命令跟其他redis命令一样,是没有加密的;阻止不了攻击者在网络上窃取你的密码;
3.3 REDIS支持的类型
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
<1> String(字符串)
string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。
各个数据类型应用场景:
https://www.runoob.com/redis/redis-data-types.html
【实例】
redis 127.0.0.1:6379> SET name "duncanwang"
OK
redis 127.0.0.1:6379> GET name
"duncanwang"
<2>Hash(哈希)
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
【实例】
127.0.0.1:6379> HMSET say field1 "Hello" field2 "world"
OK
127.0.0.1:6379> HMGET say field1
1) "Hello"
127.0.0.1:6379> HMGET say field2
1) "world"
实例中我们使用了 Redis HMSET, HGET 命令,HMSET 设置了两个 field=>value 对, HGET 获取对应 field 对应的 value。
每个 hash 可以存储 232 -1 键值对(40多亿)。
<3> List(列表)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
【实例】
127.0.0.1:6379> LPUSH mylist "duncan"
(integer) 1
127.0.0.1:6379> LPUSH mylist "jenise"
(integer) 2
127.0.0.1:6379> LPUSH mylist "dijior"
(integer) 3
127.0.0.1:6379> LRANGE mylist 0 2
1) "dijior"
2) "jenise"
3) "duncan"
列表最多可存储 232 - 1 元素 (4294967295, 每个列表可存储40多亿)。
<4> Set(集合)
Redis 的 Set 是 string 类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
SADD 命令
添加一个 string 元素到 key 对应的 set 集合中,成功返回 1,如果元素已经在集合中返回 0。
SADD key member
【实例】
127.0.0.1:6379> SADD myset "duncanwang"
(integer) 1
127.0.0.1:6379> SADD myset "jenise"
(integer) 1
127.0.0.1:6379> SADD myset "dejior"
(integer) 1
127.0.0.1:6379> SADD myset "duncanwang"
(integer) 0
127.0.0.1:6379> SMEMBERS myset
1) "dejior"
2) "jenise"
3) "duncanwang"
注意:以上实例中 rabitmq 添加了两次,但根据集合内元素的唯一性,第二次插入的元素将被忽略。
集合中最大的成员数为 232 - 1(4294967295, 每个集合可存储40多亿个成员)
<5> zset(sorted set:有序集合)
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
ZADD 命令
添加元素到集合,元素在集合中存在则更新对应score
ZADD key score member
【实例】
127.0.0.1:6379> ZADD myzset 0 "duncanwang"
(integer) 1
127.0.0.1:6379> ZADD myzset 1 "jenise"
(integer) 1
127.0.0.1:6379> ZADD myzset 2 "dejior"
(integer) 1
127.0.0.1:6379> ZADD myzset 1 "dejior"
(integer) 0
127.0.0.1:6379> ZRANGEBYSCORE myzset 0 3
1) "duncanwang"
2) "dejior"
3) "jenise"
3.4 Redis 发布订阅
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
Redis 客户端可以订阅任意数量的频道。
下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
【实例】
以下实例演示了发布订阅是如何工作的。在我们实例中我们创建了订阅频道名为 redisChat:
redis 127.0.0.1:6379> SUBSCRIBE redisChat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1
现在,我们先重新开启个 redis 客户端,然后在同一个频道 redisChat 发布两次消息,订阅者就能接收到消息。
redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique"
(integer) 1
redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by runoob.com"
(integer) 1
订阅者的客户端会显示如下消息
1) "message"
2) "redisChat"
3) "Redis is a great caching technique"
1) "message"
2) "redisChat"
3) "Learn redis by runoob.com"
Redis 发布订阅命令
下表列出了 redis 发布订阅常用命令:
序号 | 命令 | 描述 |
---|---|---|
1 | PSUBSCRIBE pattern [pattern ...] | 订阅一个或多个符合给定模式的频道。 |
2 | PUBSUB subcommand [argument [argument ...]] | 查看订阅与发布系统状态。 |
3 | PUBLISH channel message | 将信息发送到指定的频道。 |
4 | PUNSUBSCRIBE [pattern [pattern ...]] | 退订所有给定模式的频道。 |
5 | SUBSCRIBE channel [channel ...] | 订阅给定的一个或多个频道的信息。 |
6 | UNSUBSCRIBE [channel [channel ...]] | 指退订给定的频道。 |
3.5 Redis 事务
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
批量操作在发送 EXEC 命令前被放入队列缓存。
收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历以下三个阶段:
开始事务。
命令入队。
执行事务。
实例
以下是一个事务的例子, 它先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令:
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED
redis 127.0.0.1:6379> GET book-name
QUEUED
redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED
redis 127.0.0.1:6379> SMEMBERS tag
QUEUED
redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
2) "C++"
3) "Programming"
单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
Redis 事务命令
下表列出了 redis 事务的相关命令:
序号 | 命令 | 描述 |
---|---|---|
1 | DISCARD | 取消事务,放弃执行事务块内的所有命令。 |
2 | EXEC | 执行所有事务块内的命令。 |
3 | MULTI | 标记一个事务块的开始。 |
4 | UNWATCH | 取消 WATCH 命令对所有 key 的监视。 |
5 | WATCH key [key ...] | 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 |
4. REDIS常见问题
4.1 如何能够通过外网IP访问redis服务器?
默认情况下,连接Redis只能通过本地(127.0.0.1)来连接,不能通过外部IP来访问。为了支持外部IP访问,需要做响应的调整。
(1) 确认虚拟主机的安全组放开了6379端口。
(2)注释掉只能通过本地(127.0.0.1)配置
进入Redis目录打开Redis.conf配置文件,注释掉bind对应代码。
vim /etc/redis/redis.conf
# bind 127.0.0.1
# 默认不是守护进程方式运行,修改为yes
daemonize yes
#禁用保护模式
protected-mode no
kill掉已启动的进程,然后重启Redis并指明配置文件:
redis-server /etc/redis/redis.conf
4.2 redis无法向磁盘写入RDB的报错
现象:
redis set failed: MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs for details about the RDB error.
分析:
日志显示:overcommit_memory被设置成0,redis后台的保存操作在低可用内存的情况下会失败。这个时候很清楚,并不是磁盘被占满的缘故,事实上磁盘剩余量还很多。
查了下,内核参数 vm.overcommit_memory 接受三种取值:
0 – Heuristic overcommit handling. 这是缺省值,它允许overcommit,但过于明目张胆的overcommit会被拒绝,比如malloc一次性申请的内存大小就超过了系统总内存。Heuristic的意思是“试探式的”,内核利用某种算法(对该算法的详细解释请看文末)猜测你的内存申请是否合理,它认为不合理就会拒绝overcommit。
1 – Always overcommit. 允许overcommit,对内存申请来者不拒。
2 – Don’t overcommit. 禁止overcommit。
CommitLimit 就是overcommit的阈值,申请的内存总数超过CommitLimit的话就算是overcommit。
解决方案:
1、打开redis配置文件
$ sudo vi /etc/redis/redis.conf # 注意vi后面有空格
在下图此行中将yes改为no,保存退出
2、指定配置加载文件
$ sudo redis-server /etc/redis/redis.conf
$ redis-cli
以上就是解决redis无法写入数据的问题。
4.2 redis如何查看所有键值的内容?
KEYS * 即可查看。
4.3 redis常见性能问题和解决方案?
(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…
这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
4.4 MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?
相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略:
voltile-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(驱逐):禁止驱逐数据;
5.Redis面试题
(1)吐血整理60个Redis面试题,全网最全了
https://zhuanlan.zhihu.com/p/93515595
(2)面圈网-真题
http://www.mianshigee.com/question/search/?q=redis
6.参考
(1)官网
https://redis.io/
(2)redis中文网
http://www.redis.cn/documentation.html
(3)Redis 教程
https://www.runoob.com/redis/redis-tutorial.html
(4)Redis 服务器
https://www.runoob.com/redis/redis-server.html
(5)Redis 数据备份与恢复
https://www.runoob.com/redis/redis-backup.html
(6)Redis 持久化
一起看懂Redis两种持久化方式的原理
https://segmentfault.com/a/1190000015983518?utm_source=tag-newest
(6) memcache和redis原理对比
https://www.kancloud.cn/mayan0718/php/555555
(7) Redis底层数据结构 - 跳跃表
https://www.jianshu.com/p/0206ef2cffa4