Redis(二)
管道
可以使用nc连接redis, 只要连接到redis对应的端口, 比如6379, 就可以使用redis中的命令.
通过echo将多个命令组合起来, echo可以识别\n
, 使用\n将多条命令拼起来, 在一次网络请求发送过去, 节约多次网络IO的消耗.
预加载
redis启动之后是空的, 无数据的. 有时我们想对redis启动后添加一些数据, 或者短时间内想要添加一大堆数据.
固然, 我们可以使用管道的方式, 一次批量的传入多条命令上去,
然而这并不是一个非常可靠的方式,因为用netcat进行大规模插入时不能检查错误。从Redis 2.6开始redis-cli
支持一种新的被称之为pipe mode的新模式用于执行大量数据插入工作。
cat data.txt | redis-cli --pipe
data.txt会有一些格式要求.
Pub/Sub
发布/订阅是对多个订阅者的广播, 若消息已经发出去, 订阅者在消息发送之后订阅, 将不会收到该消息.
消息分为实时消息和历史消息. 实时消息是本来就应该接受到的, 历史消息又分为近期消息和更老的消息.
实时消息可以使用pub/sub实现.
全部历史消息肯定要放在数据库中的, 磁盘会更便宜.
近期的历史纪录被查询的概率会稍大, 可以放到redis中缓存, 使用sorted set, 按时间排序.
事务
使用multi
开启事务, 使用exec
提交事务, discard
放弃事务.
redis中没有提供事务回滚.
watch
可以提供一个乐观锁的实现, watch可以监听一个key, 当这个key改变的时候, 事务取消.
为什么 Redis 不支持回滚(roll back)
如果你有使用关系式数据库的经验, 那么 “Redis 在事务失败时不进行回滚,而是继续执行余下的命令”这种做法可能会让你觉得有点奇怪。
以下是这种做法的优点:
- Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
- 因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。
有种观点认为 Redis 处理事务的做法会产生 bug , 然而需要注意的是, 在通常情况下, 回滚并不能解决编程错误带来的问题。 举个例子, 如果你本来想通过 INCR命令将键的值加上 1 , 却不小心加上了 2 , 又或者对错误类型的键执行了 INCR, 回滚是没有办法处理这些情况的。
布隆过滤器
redis提供了布隆过滤器的相关模块, 只要加载上就可以直接使用布隆过滤器而不用客户端再自己实现相应算法了.
内存大小
redis可以支持设置最大内存, 因为redis提供淘汰算法, 如果内存过大, 淘汰算法的效率就会下降, 一般在1G-10G之间.
最大内存可以通过配置文件中的 maxmemory
淘汰策略:
volatile-lru:从已设置过期时间的内存数据集中挑选最近最少使用的数据 淘汰;
volatile-ttl: 从已设置过期时间的内存数据集中挑选即将过期的数据 淘汰;
volatile-random:从已设置过期时间的内存数据集中任意挑选数据 淘汰;
allkeys-lru:从内存数据集中挑选最近最少使用的数据 淘汰;
allkeys-random:从数据集中任意挑选数据 淘汰;
no-enviction(驱逐):禁止驱逐数据。(默认淘汰策略。当redis内存数据达到maxmemory,在该策略下,直接返回OOM错误);
过期
redis也会给key提供过期功能, 使用expire
来设定过期的秒数. 也可以使用expireat
来设定过期时间.
当重新设置value时, 会将过期时间覆盖掉.
// 使用set会覆盖过期时间
127.0.0.1:6379> set k1 hello
OK
127.0.0.1:6379> expire k1 300
(integer) 1
127.0.0.1:6379> ttl k1
(integer) 298
127.0.0.1:6379> set k1 word
OK
127.0.0.1:6379> ttl k1
(integer) -1使用incr,lpush等命令,修改一个值而不是替代它的话, 不会覆盖过期时间
127.0.0.1:6379> set k1 10
OK
127.0.0.1:6379> expire k1 300
(integer) 1
127.0.0.1:6379> ttl k1
(integer) 297
127.0.0.1:6379> incr k1
(integer) 11
127.0.0.1:6379> ttl k1
(integer) 292
Redis如何淘汰过期的keys
Redis keys过期有两种方式:被动和主动方式。
当一些客户端尝试访问它时,key会被发现并主动的过期。
当然,这样是不够的,因为有些过期的keys,永远不会访问他们。 无论如何,这些keys应该过期,所以定时随机测试设置keys的过期时间。所有这些过期的keys将会从密钥空间删除。
具体就是Redis每秒10次做的事情:
- 测试随机的20个keys进行相关过期检测。
- 删除所有已经过期的keys。
- 如果有多于25%的keys过期,重复步奏1.
这是一个平凡的概率算法,基本上的假设是,我们的样本是这个密钥控件,并且我们不断重复过期检测,直到过期的keys的百分百低于25%,这意味着,在任何给定的时刻,最多会清除1/4的过期keys。
数据双写
如果要用redis做数据库, 因为数据库的数据是绝对不能丢的, 所以要解决内存掉电的情况下, 数据怎么办.
若是redis做数据库, 和mysql一起用, 如何解决数据双写的问题.
redis可以实现持久化.
持久化
在存储层, 一般会有两部分:
快照/副本
RDB
redisDB, RDB是时点性的.
当redis生成快照的时候,由于需要时间,并且对外服务不能停止,因此复制的过程中,数据在不断更新,因此当快照完成的时候,这个快照具体应该说成是哪个时点的数据就会很混乱。
在linux中, 父进程中的数据可以使子进程看到, 而且父进程和子进程中互相修改也不会受到影响.
如果在持久化的时候, 使用一个子进程进行落盘, 根据父进程子进程不互相影响, 是可以实现非阻塞式的持久化.
那么问题在于, 创建这个子进程的速度有多快, 创建这个子进程是否需要父进程相同的内存来复制数据.
在linux中有一个系统调用
fork
, 他可以实现速度快, 占用空间小.应用程序使用虚拟地址完成对物理地址的映射,
子进程是拷贝了父进程, 子进程的虚拟地址和父进程指向同一个物理地址, 这里相当于复制了一份指针.
按照我们java的知识, 某个引用做出改变后, 所有的引用都会改变. 但是这里使用了
CopyOnWrite
, 写时复制.创建进程时并不发生复制, 只有当父进程想要修改时才做复制, 这样的好处是创建进程变快了, 而且不可能子进程中所有的内存都发生改变, 因此所需要的内存就少了.
如何触发持久化
save
命令可以触发阻塞持久化,bgsave
可以触发非阻塞持久化.还可以在配置文件中配置bgsave的规则, 配置文件中的save就是bgsave.
################################ SNAPSHOTTING ################################
#
# Save the DB on disk:
#
# save
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving completely by commenting out all “save” lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save “”save 900 1
save 300 10
save 60 10000# 在给定时间内达到一定量的操作数的时候, 就会开始持久化
RDB的弊端
- 不支持拉链, 只有一个dump.rdb
- 丢失数据相对多, 时点与时点之间的窗口数据容易丢失
RDB的优势
恢复速度较快, 类似于java中的序列化.
日志
AOF
append only file
只会向文件追加, 会将redis的写操作记录到文件中.
AOF的优势
丢失数据少
AOF的劣势
- 体量无限变大
- 恢复慢
redis中RDB和AOF可以同时开启, 如果开启了AOF, 只会用AOF恢复.
4.0以后, AOF中包含RDB全量, 增加记录新的写操作.
AOF这种方式优势是显而易见的, 数据保存性高.
在4.0以前, 当AOF达到一定大小后进行重写, 会删除抵消的命令, 合并重复的命令.
在4.0以后, 会将老的数据RDB到AOF文件中, 将增量的以指令的方式Append到AOF.
如何开启AOF
redis默认是关闭AOF的, 只要在配置文件中, 将appendonly
改成yes就可以开启AOF.
还有一个配置是appendfsync
, 在linux中 , 程序写入硬盘是需要调用内核的, 内核什么时候将内存中修改的值刷到磁盘上就不一定了.
这个配置是用来配置什么时候刷到磁盘上.
这个配置有三个取值
always
everysec
no
AOF文件达到多大时进行重写.
auto-aof-rewrite-min-size
是第一次重写的大小, 当到达auto-aof-rewrite-percentage
%时触发一次重写, 然后redis会记录重写后的大小, 下次达到auto-aof-rewrite-percentage
%的时候再次进行重写.auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb