Redis 学习笔记
Redis 简介
Redis遵守BSD协议, 是一个基于内存的高性能key-value数据库。
特点:
- 支持数据持久化,可以把内存中的数据保存到磁盘(数据备份),重启的时候可以再次加载使用(数据恢复)。
- 不仅仅支持简单的key-value类型,提供string,list,set,zset,hash多种数据结构。
- 支持数据备份——+master-slave模式数据备份
Redis优势:
- 性能极高,读速度110000次/s,写速度81000次/s。
- 丰富数据类型。
- 原子性,单个命令是原子性的。多个操作支持事务,事务中是非原子性的。
- 丰富的特性,比如缓存、发布订阅(publish \ subscribe)、消息、按key设置过期时间,过期后将自动删除。
Redis与其他key-value存储有什么不同?
- Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
- Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。
注:(原子性:要么成功执行要么失败完全不执行)
redis中的字符串类型String:
String类型是redis最基本的类型,是二进制安全的,可以包含任何数据,例如jpg图片,或者序列化的对象。数字值在 Redis 中也以字符串的形式保存。
一个键最大能存储 512 MB,格式为 key : value
命令:set \ get \ mset \ INCR
设置多个key:mset key1 value1 key2 value2 key3 value3
对数字值加 1 :INCR key
redis中的 hash 类型:
一个 redis hash 可以存储40多亿个键值对,hash 是键值对的集合。
特别适用于存储对象。
命令:hmset \ hmget \ hset \ hget \ hsetnx \ hgetall \ hdel \ hkeys \ hlen \ hexists \ hvals
设置一个人的信息: hmset hash_table1 name "zhangsan" age "18" sex "women"
获取: hget hash_table1 name
获取: hget hash_table1 age
获取: hget hash_table1 sex
redis中的 list 类型:
list 是一个简单的字符串列表,按照插入顺序排序(栈),也可以向收尾添加元素,最多存储40多亿个。
命令:lpush \ rpush \ lpop \ rpop \ blpop \ brpop \ lrange \ llen \ lindex
向 list_table1 表中插入字符串:lpush list_table1 "abc"
向 list_table1 表中插入字符串:lpush list_table1 "efg"
向 list_table1 表中插入字符串:lpush list_table1 "xyz"
从栈顶取出一个:lpop list_table1
范围查询: lrange list_table1 0 10
blpop移出并获取列表的第一个元素,提供一个等待时间,list没有元素会阻塞list直到等待超时或发现可弹出元素为止。blpop list 100
brpop移出并获取列表的最后一个元素,提供一个等待时间,list没有元素会阻塞list直到等待超时或发现可弹出元素为止。brpop list 100
redis 中的 set 类型:
set 集合是存储 string 类型的无序集合,集合是用哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
添加元素到集合中,如果成功返回 1,如果已存在返回 0(插入被忽略),一个集合最多可以存40多亿个 string 。
命令:sadd \ smembers
插入:sadd set_table1 "abc"
插入:sadd set_table1 "efg"
插入:sadd set_table1 "zzz"
插入:sadd set_table1 "zzzzz"
插入:sadd set_table1 "zzzzz" (已存在,操作将被忽略)
查看已有成员:smembers set_table1
输出(无序):
1) "zzzzz"
2) "zzz"
3) "efg"
4) "abc"
redis 中的 zset 类型:
zset 是有序集合。和 set 只有一点不同:zset 中的每个元素都会关联一个 double 类型的分数(score),redis 通过这个 score 对成员进行由小到大排序。
其中 score 可以重复。
命令:zadd \ zrangebyscore
格式:zadd 集合表 分数 string
插入:zadd zset_table1 1 "abc"
插入:zadd zset_table1 2 "bcd"
插入:zadd zset_table1 7 "aaa"
插入:zadd zset_table1 100 "abb"
插入:zadd zset_table1 -10 "zzz"
按分数排序:zrangebyscore ddd-100 200
输出(有序):
1) "zzz"
2) "abc"
3) "bcd"
4) "aaa"
5) "abb"
删除一个key:del keyname (1/0)
序列化一个key,返回序列化的值: dump keyname
判断一个key是否存在:exists keyname (1/0)
给一个key设置过期时间,过期会自动删除:
1\expire keyname second (/s) (1/0)
2\pexpire keyname milliseconds (/ms) (1/0)
3\expireat keyname Unix时间戳 (1/0)
查看一个key剩余过期时间::
1\ttl keyname (/s)
2\pttl keyname (/s)
ps:ttl( time to live )
删除一个key的过期时间,让他永不过期:persist keyname (1/0)
按条件查找key,返回一个列表:keys 条件
set d1 100
set d2 200
set d3 300
set c 500
keys * (会输出所有)
keys d* (会输出:d1,d2,d3)
keys c* (会输出:c)
把一个key从当前数据移动到另一个数据库:move keyname 数据库编号 (1/0)
ps:数据库编号默认为0,即默认使用0数据库;可以使用select n 选择其他数据库,如select 1 ;select 3;
set d 100
move d 3
WATCH命令:
watch 是在一个事务执行前监控一个或多个 key,如果在 watch 之后有任何一个 key 发生了变化,EXEC 命令执行的事务将会被放弃,并返回(nil)提示事务执行失败。
例如:
set num 1
WATCH
get num
set num 5
MULTI
set num 10
EXEC
num 在 WATCH 后被修改,此时会放弃执行 EXEC 事务。
从数据库中随机返回一个key:randomkey (keyname/nil)
清空数据库:flushdb
重命名key:rename oldkey newkey
1\oldkey不存在 (error)
2\newkey已存在,oldkey值会覆盖newkey
在newkey不存在时才能重命名成功,防止key覆盖:renamenx oldkey newkey
查看一个key的类型:type key
Redis发布订阅功能:
它是一种消息通信模式,发送者发送(publish)消息,订阅者接收(subscribe)消息。其中Redis客户端可以订阅任意数量的频道。
命令:subscribe \ publish
客户端订阅频道(接收消息):subscribe redisChat
重新开启一个redis客户端(发布消息):publish redisChat "Hello,everyone ! Good morning ! "
订阅者会收到发送者发布的消息。
Redis事务:
事务可以一次执行多个命令。任意一条命令的失败不会影响其他命令的执行。
Redis命令的执行是原子性的,但Redis事务执行是非原子性的。
一个事务从开始到执行会经历三个阶段:1 开始事务——2 命令入队——3 执行事务
命令:multi / exec / discard / unwatch
redis 127.0.0.1:7000> multi (标记一个事务块的开始) OK redis 127.0.0.1:7000> set name "zhangsan" (放入队列缓存) QUEUED redis 127.0.0.1:7000> get name (放入队列缓存) QUEUED redis 127.0.0.1:7000> set age 18 (放入队列缓存) QUEUED redis 127.0.0.1:7000> exec (执行事务块内的命令) 1) OK 2) OK 3) OK
注:可以在 exec 命令前调用 discard 取消事务,放弃执行事务块内的命令。
Redis脚本:
Redis 脚本使用 Lua 解释器来执行脚本。 Reids 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。
如需详细学习:看文档(/手动微笑)
Redis服务器:
在后台异步保存当前数据库的数据到磁盘:BGSAVE
同步保存数据到磁盘:SAVE
删除当前数据库的所有key:flushdb
删除所有数据库的所有key:flushall
Redis数据备份与恢复:
redis数据保存在安装目录dump.rdb文件。
备份:SAVE / BGSAVE
恢复:如果 A 机器坏了需要恢复数据,先停止 A Redis服务,把备份的 dump.rdb 文件拷贝到 A Redis的安装目录下,启动A Redis服务即可。
查看Redis安装目录:config get dir
Redis安全:
没有设置密码时,客户端可直接使用 Redis 服务。
设置密码后,客户端需验证密码后才能使用 Redis 服务。
Redis 服务器查看密码:config get requirepass (“”/密码)
Redis 服务器设置密码:config set requirepass password
Redis 客户端验证密码:AUTH password
Redis性能测试:
Redis 性能测试是通过同时执行多个命令实现的。
命令:redis-benchmark
同时执行10000个请求检测性能: redis-benchmark -n 10000 -q
仅测试set,get lpush,hgetall命令的性能:redis-benchmark -t set,lpush -n 10000 -q
参数解释:
Redis 客户端连接:
redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作:
- 首先,客户端 socket 会被设置为非阻塞模式,因为 Redis 在网络事件处理上采用的是非阻塞多路复用模型。
- 然后为这个 socket 设置 TCP_NODELAY 属性,禁用 Nagle 算法
- 然后创建一个可读的文件事件用于监听这个客户端 socket 的数据发送
Redis服务中默认的最大连接数是10000,可以直接在 redis.conf 中修改这个值,也可以使用命令修改:config set maxclients 60000
Redis 管道技术:
Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。
优势:提高 redis 服务的性能。
Redis分区:
分区是分割数据到多个Redis服务器的过程,因此每个服务器只保存key的一个子集。
分区的优势:
- 利用多台计算机的内存,可以构造更大的数据库。
- 利用多核和多台计算机,可以拓展计算能力。
- 李彤多台计算机和网络适配器,可以拓展网络带宽。
分区的不足:分区导致redis的一些特性表现的不是很好,如下
- 不支持多个key的操作。举例来说,当两个set映射到不同的redis实例上时,你就不能对这两个set执行交集操作。
- 涉及多个key的redis事务不能使用。
- 当使用分区时,数据处理较为复杂,比如你需要处理多个rdb/aof文件,并且从多个实例和主机备份持久化文件。
- 增加或删除容量也比较复杂。redis集群大多数支持在运行时增加、删除节点的透明数据平衡的能力,但是类似于客户端分区、代理等其他系统则不支持这项特性。然而,一种叫做presharding的技术对此是有帮助的。
分区类型:
Redis 有两种分区类型,范围分区、哈希分区。
假设有4个Redis服务器,R0,R1,R2,R3,有许多key等待被划分到指定服务器上。
范围分区法:映射一定范围的对象到特定的Redis服务器。
比如,ID从0到10000的用户会保存到实例R0,ID从10001到 20000的用户会保存到R1,以此类推。这种方式是可行的,并且在实际中使用,不足就是要有一个区间范围到实例的映射表。这个表要被管理,同时还需要各种对象的映射表,通常对 Redis来说不是好的方法。
哈希分区法:对任何key都适用,也无需是object_name:这种形式。
- 用一个hash函数将key转换为一个数字,比如使用crc32 hash函数。对key foobar执行crc32(foobar)会输出类似93024922的整数。
- 对这个整数取模,将其转化为0-3之间的数字,就可以将这个整数映射到4个Redis实例中的一个了。93024922 % 4 = 2,就是说key foobar应该被存到R2实例中。
Redis 数据持久化 3 种方法:
- RDB 持久化(快照),称为“半持久化模式”
原理是 fork 一个子进程以时间间隔将 Redis 在内存中的数据保存到临时RDB文件中,完成后,用其替代老文件(dump.rdb)。
优点:
a. 相比于 AOF 备份数据,RDB 速度更快。
b. 相比于 AOF 恢复数据,RDB 速度更快。
c. RDB 整个Redis数据库只包含一个文件,对于文件备份而言更友好;数据恢复也方便,只需要把这个文件拷贝到其他存储介质即可。
缺点:
a. RDB 数据安全可靠性低,当系统意外停止时没来得及写入磁盘的数据将会丢失。
b. RDB 通过 fork 子进程来持久化数据,如果数据集很大,会导致服务器暂停服务几百毫秒。
RDB 持久化配置信息:
save 900 1 # 在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。
save 300 10 # 在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
save 60 10000 # 在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。
- AOF 持久化,称为“全持久化模式” (Append Only File)
原理是将 Redis 的操作日志以追加的方式写入文件。
优点:
a. AOF 更高的数据安全可靠性,分为 3 中同步策略,“每秒同步”,“每次修改同步”,“不同步”。
b. AOF 以 append 方式写入日志文件,即使写入过程中出现宕机也不会破环日志文件中已有内容,并且可以在 Redis 重启前使用 redis-check-aof 工具解决数据一致性问题。
c. AOF 包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作,我们也可以通过该文件完成数据的重建。
缺点:
a. 相同的数据量,AOF 文件比 RDB 大。
b. 恢复大数据集比 RDB 慢。
c. Redis 使用 AOF 模式持久化时运行效率比 RDB 慢,修改一次就同步一次效率低,但数据安全性高;每秒同步一次效率还是比较高的;不同步效率和 RDB 差不多 哈哈哈嗝~
AOF 持久化配置信息:
appendfsync always # 每次有数据修改发生时都会写入AOF文件。
appendfsync everysec # 每秒钟同步一次,该策略为AOF的缺省策略。
appendfsync no # 从不同步。高效但是数据不会被持久化。
- 混合模式 (4.0版本支持)
解决了 AOF 恢复慢,RDB 写入时间间隔的问题。
Redis 缓存失效策略:(以下全是引用)
缓存系统都要定期清理无效数据,这就需要“淘汰策略”打辅助了。
在 Redis 中,有生存期的 key 被称为 volatile,在创建缓存时,要为 key 设置生存期,过期后 key 会被删除。
当 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(驱逐):禁止驱逐数据
volatile 和 allkeys 规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据,后面的 lru、ttl 以及 random 是三种不同的淘汰策略,再加上一种 no-enviction 永不回收的策略。
使用策略规则:
- 如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用 allkeys-lru。
- 如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用 allkeys-random。
三种数据淘汰策略:
ttl 和 random 比较容易理解,实现也比较简单。Lru 最近最少使用淘汰策略设计上会对 key 按失效时间排序,然后取最先失效的 key 进行淘汰。
Redis适用场景:
- 会话缓存(Session Cache)
- 全页缓存(FPC)
- 队列。例如,Celery有一个后台就是使用Redis作为broker
- 排行榜/计数器
- 发布/订阅
最后:在 Redis 实际使用中一定要配置允许最大使用内存大小,默认没有限制(0),如果服务器内存已经满了再有新数据插入进来,Redis 会崩溃。
参考了:Redis的那些最常见面试问题
总结:
经过这几天的学习,对 Redis 数据库有了一个大体的认识和基本的使用,同时还有很多不懂的地方,希望以后可以更完整的学习并在实际应用中使用。