目录
一、概述
1、管道(Pipelining)
管道(Pipelining):怎么一次性发送多个命令,节省往返时间。
[root@localhost ~]# yum install nc #安装nc
Loaded plugins: fastestmirror, product-id, search-disabled-repos, subscription-
: manager
........
Installed:
nmap-ncat.x86_64 2:6.40-19.el7
Complete!
[root@localhost ~]# nc localhost 6379
auth 123456
+OK
keys *
*4
$2
k2
$2
k3
$2
k4
$2
k1
set k8 h1
+OK
^C
[root@localhost ~]# echo -e "auth 123456\nset k9 99 \nincr k9\n get k9" | nc localhost 6379
+OK
+OK
:100
$3
100
[root@localhost ~]#
2、发布/订阅(Redis Pub/Sub)
发布/订阅(Redis Pub/Sub):Redis是一个快速稳定的发布/订阅消息系统!
发布/订阅:实时性
127.0.0.1:6379> help @pubsub
PSUBSCRIBE pattern [pattern ...]
summary: Listen for messages published to channels matching the given patterns
since: 2.0.0
PUBLISH channel message
summary: Post a message to a channel
since: 2.0.0
PUBSUB
summary: A container for Pub/Sub commands
since: 2.8.0
PUBSUB CHANNELS [pattern]
summary: List active channels
since: 2.8.0
PUBSUB HELP
summary: Show helpful text about the different subcommands
since: 6.2.0
PUBSUB NUMPAT
summary: Get the count of unique patterns pattern subscriptions
since: 2.8.0
PUBSUB NUMSUB [channel [channel ...]]
summary: Get the count of subscribers for channels
since: 2.8.0
PUBSUB SHARDCHANNELS [pattern]
summary: List active shard channels
since: 7.0.0
PUBSUB SHARDNUMSUB [shardchannel [shardchannel ...]]
summary: Get the count of subscribers for shard channels
since: 7.0.0
PUNSUBSCRIBE [pattern [pattern ...]]
summary: Stop listening for messages posted to channels matching the given patterns
since: 2.0.0
SPUBLISH shardchannel message
summary: Post a message to a shard channel
since: 7.0.0
SSUBSCRIBE shardchannel [shardchannel ...]
summary: Listen for messages published to the given shard channels
since: 7.0.0
SUBSCRIBE channel [channel ...] #监听通道后,才能接收到通道的消息
summary: Listen for messages published to the given channels
since: 2.0.0
SUNSUBSCRIBE [shardchannel [shardchannel ...]]
summary: Stop listening for messages posted to the given shard channels
since: 7.0.0
UNSUBSCRIBE [channel [channel ...]]
summary: Stop listening for messages posted to the given channels
since: 2.0.0
127.0.0.1:6379> PUBLISH channel1 hello #此时没有订阅此通道,不会接收到此消息
(integer) 0
127.0.0.1:6379> PUBLISH channel1 he # 执行此命令前再开启一个客户端,订阅此通道
(integer) 1
127.0.0.1:6379>
#另一个客户端
127.0.0.1:6379> SUBSCRIBE channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1
1) "message"
2) "channel1"
3) "he" #订阅到he
3、事物
Redis使用 MULTI 命令标记事务开始,它总是返回OK。MULTI执行之后,客户端可以发送多条命令,Redis会把这些命令保存在队列当中,而不是立刻执行这些命令。所有的命令会在调用EXEC 命令之后执行。
如果不调用EXEC,调用 DISCARD 会清空事务队列并退出事务。
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k31 aaa
QUEUED
127.0.0.1:6379(TX)> set k32 bbb
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
127.0.0.1:6379>
为什么Redis不支持回滚?
如果你了解关系数据库,那么Redis事务处理机制看起来有点奇怪。
Redis事务中的命令允许失败,但是Redis会继续执行其它的命令而不是回滚所有命令。
这么做的原因有两点:
Redis 命令只在两种情况失败:
语法错误的时候才失败(在命令输入的时候不检查语法)。
要执行的key数据类型不匹配:这种错误实际上是编程错误,这应该在开发阶段被测试出来,而不是生产上。
因为不需要回滚,所以Redis内部实现简单并高效。
当出现bug的时候Redis的这种做法并不友好,可是需要注意的是回滚并不能解决程序bug。
例如,对于需要增加1的逻辑增加了2,或者操作的key类型不对,这些情况回滚并没有什么帮助。
考虑到没有人能避免程序员错误,并且这种错误也基本不能进入生产环境,我们选择了更简单且更高效的方法,不支持错误回滚。
4、RedisBloom
Redis官网 ---模块(modules)---RedisBloom
RedisBloom类似一个插件,引入到redis中来使用,需要安装
解决缓存穿透问题。
缓存穿透:某网站商品,搜索此网站没有的商品,大量请求,请求就会透过缓存到达数据库访问。
RedisBloom使用:bitmap把已有的商品存储下来,映射函数进行映射,概率解决问题,不能100%解决,概率<1%阻挡。1.你有什么2.向bitmap中标记3.请求可能被吴标记4.但是一定概率减少放行,穿透5.成本低
客户端:实现bloom算法,自己承载bitmap------------------redis
客户端:实现bloom算法------------------redis:bitmap
客户端------------------redis:bloom.so,bitmap
数据库增加新元素-->完成对bloom的添加
补充:?
bloom
counting bloom
cukcoo
布谷鸟过滤器
bloom
1.访问redis.io
2.modules
3.访问RedisBloom的github
4.linux wget *.zip
5.yum install unzip
6.unzip *.zip
7.make
8.cp bloom.so /.../redis/
9.redis-server --loadmodule /..../redis/redisbloom.so
10.redis-cli
11.bf.add k1 abc
bf.exits abc
bf.exits sdddd
12.cf.add #布谷鸟过滤器
5、redis作为数据库缓存的区别
redis作为数据库缓存的区别?
1、缓存数据不重要,不是全量数据,缓存应该随着访问变化热数据
2、redis作为缓存,redis里的数据怎么能随着业务变化,只保留热数据,因为内存大小是有限的,也就是瓶颈
①、业务逻辑:key的有效期
1、会随着访问延长?不对
2、发生写,会剔除过期时间
3、倒计时,且redis不能延长
4、定时
②、业务运转:内存是有限的,随着访问的变化,应该淘汰掉冷数据
内存多大呢?maxmemory <bytes>
mamemory-policy noeviction
内存淘汰策略
LFU:碰了多少次
LRU:多久没碰它
内存中的内容:掉电易失
6、过期(Expires)
过期(Expires) Redis允许对每个key设置存活的时间,以便于当到期时间过了自动删除这些Key。
127.0.0.1:6379> set k1 hello
OK
127.0.0.1:6379> get k1
"hello"
127.0.0.1:6379> EXPIRE k1 20 #过期时间 秒
(integer) 1
127.0.0.1:6379> ttl k1
(integer) 17
127.0.0.1:6379> ttl k1
(integer) 13
127.0.0.1:6379> set k1 20 #发生写,剔除过期时间
OK
127.0.0.1:6379> ttl k1
(integer) -1
127.0.0.1:6379> time
1) "1684078721"
2) "487550"
127.0.0.1:6379> EXPIREAT k1 1684078821
(integer) 1
127.0.0.1:6379> ttl k1
(integer) 15
127.0.0.1:6379> ttl k1
(integer) 10
127.0.0.1:6379> ttl k1
(integer) 6
127.0.0.1:6379> ttl k1
(integer) 1
127.0.0.1:6379> ttl k1
(integer) -2
127.0.0.1:6379> ttl k1
(integer) -2
127.0.0.1:6379> get k1
(nil)
127.0.0.1:6379>
Redis 如何淘汰过期 Key
Redis 的 key 有两种过期淘汰的方式:被动方式、主动方式。被动过期:用户访问某个 key 的时候,key 被发现过期。
当然,被动方式过期对于那些永远也不会再次被访问的 key 并没有效果。不管怎么,这些 key 都应被过期淘汰,所以 Redis 周期性主动随机检查一部分被设置生存时间的 key,那些已经过期的 key 会被从 key 空间中删除。
Redis每秒执行10次下面的操作:
1、从带有生存时间的 key 的集合中随机选 20 进行检查。
2、删除所有过期的key。
3、如20里面有超过25%的key过期,立刻继续执行步骤1。
这是一个狭义概率算法,我们假设我们选出来的样本 key 代表整个 key 空间,我们继续过期检查直到过期 key 的比例降到 25% 以下。这意味着在任意时刻已经过期但还占用内存的 key 的数量,最多等于每秒最多写操作的四分之一。
7、管道/父子进程
[root@localhost ~]# num=0
[root@localhost ~]# echo $num
0
[root@localhost ~]# ((num++))
[root@localhost ~]# echo $num
1
[root@localhost ~]# ((num++)) | echo ok
ok
[root@localhost ~]# echo $num
1
[root@localhost ~]# echo $$ #$$获取当前进程
1123
[root@localhost ~]# echo $$ | more #原因:因为$$的优先级是高于|
1123
[root@localhost ~]# echo $BASHPID #$BASHPID获取当前进程
1123
[root@localhost ~]# echo $BASHPID | more
1566
[root@localhost ~]#
管道:
1、衔接前一个命令的输出作为后一个命令的输入
2、管道会触发创建[子进程]
使用linux的时候:有父子进程
父进程的数据,子进程可不可以看得到?
常规思想,进程是数据隔离的!进阶思想,父进程其实可以让子进程看到数据!
linux中export的环境变量,子进程的修改不会破坏父进程
父进程的修改也不会破坏子进程
[root@localhost ~]# echo $$
1123
[root@localhost ~]# echo $num
1
[root@localhost ~]# /bin/bash #进入子进程
[root@localhost ~]# echo $$ #子进程
1753
[root@localhost ~]# echo $num #获取不到num
[root@localhost ~]# exit #退出子进程
exit
[root@localhost ~]# echo $num
1
[root@localhost ~]# echo $$
1123
[root@localhost ~]# export num #export
[root@localhost ~]# echo $num
1
[root@localhost ~]# /bin/bash
[root@localhost ~]# echo $num
1
[root@localhost ~]# echo $$
1793
[root@localhost ~]# vi test.sh
##======》插入:
###################=======[root@localhost ~]# vi test.sh #test.sh内容如下
#!/bin/bash
echo $$
echo $num
num=999
echo num:$num
sleep 20
echo $num
###################=======
[root@localhost ~]# chmod +x test.sh
#######子进程修改不影响父进程
[root@localhost ~]# ./test.sh & #&:在后台运行
[1] 1810
[root@localhost ~]# 1810
1
num:999
[root@localhost ~]# echo $num #上面脚本text.sh开启子进程,修改num 不会影响到父进程
1
[root@localhost ~]# 999
[1]+ Done ./test.sh
#######父进程修改不影响子进程
[root@localhost ~]# echo $num
1
[root@localhost ~]# ./test.sh &
[1] 1825
[root@localhost ~]# 1825
1
num:999
[root@localhost ~]# echo $num
1
[root@localhost ~]# num=888
[root@localhost ~]# echo $num
888
[root@localhost ~]# 999
[1]+ Done ./test.sh
[root@localhost ~]#
创建子进程的速度应该是什么程序?如果父进程是redis,内存数据比如10G 1、速度2、内存空间够不够--->fork()---> 1、速度:快 2、空间:小
Under Linux, fork() is implemented using copy-on-write pages
copy on write:内核机制,写时复制
创建子进程并不发生复制,使用指针。
[root@localhost ~]# man fork
FORK(2) Linux Programmer's Manual FORK(2)
NAME
fork - create a child process
SYNOPSIS
#include <unistd.h>
pid_t fork(void);
DESCRIPTION
fork() creates a new process by duplicating the calling process. The
new process, referred to as the child, is an exact duplicate of the
calling process, referred to as the parent, except for the following
points:
* The child has its own unique process ID, and this PID does not match
the ID of any existing process group (setpgid(2)).
* The child's parent process ID is the same as the parent's process
ID.
* The child does not inherit its parent's memory locks (mlock(2),
mlockall(2)).
* Process resource utilizations (getrusage(2)) and CPU time counters
(times(2)) are reset to zero in the child.
* The child's set of pending signals is initially empty (sigpend‐
ing(2)).
* The child does not inherit semaphore adjustments from its parent
(semop(2)).
* The child does not inherit record locks from its parent (fcntl(2)).
* The child does not inherit timers from its parent (setitimer(2),
alarm(2), timer_create(2)).
* The child does not inherit outstanding asynchronous I/O operations
from its parent (aio_read(3), aio_write(3)), nor does it inherit any
asynchronous I/O contexts from its parent (see io_setup(2)).
The process attributes in the preceding list are all specified in
POSIX.1-2001. The parent and child also differ with respect to the
following Linux-specific process attributes:
* The child does not inherit directory change notifications (dnotify)
from its parent (see the description of F_NOTIFY in fcntl(2)).
....................
EAGAIN fork() cannot allocate sufficient memory to copy the parent's
page tables and allocate a task structure for the child.
EAGAIN It was not possible to create a new process because the caller's
RLIMIT_NPROC resource limit was encountered. To exceed this
limit, the process must have either the CAP_SYS_ADMIN or the
CAP_SYS_RESOURCE capability.
ENOMEM fork() failed to allocate the necessary kernel structures
because memory is tight.
ENOSYS fork() is not supported on this platform (for example, hardware
without a Memory-Management Unit).
CONFORMING TO
SVr4, 4.3BSD, POSIX.1-2001.
NOTES
Under Linux, fork() is implemented using copy-on-write pages, so the
only penalty that it incurs is the time and memory required to dupli‐
cate the parent's page tables, and to create a unique task structure
for the child.
Since version 2.3.3, rather than invoking the kernel's fork() system
call, the glibc fork() wrapper that is provided as part of the NPTL
threading implementation invokes clone(2) with flags that provide the
same effect as the traditional system call. (A call to fork() is
equivalent to a call to clone(2) specifying flags as just SIGCHLD.)
The glibc wrapper invokes any fork handlers that have been established
using pthread_atfork(3).
EXAMPLE
See pipe(2) and wait(2).
SEE ALSO
clone(2), execve(2), exit(2), setrlimit(2), unshare(2), vfork(2),
wait(2), daemon(3), capabilities(7), credentials(7)
COLOPHON
This page is part of release 3.53 of the Linux man-pages project. A
description of the project, and information about reporting bugs, can
be found at http://www.kernel.org/doc/man-pages/.
Linux 2013-03-12 FORK(2)
8、Redis持久化
什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
Redis 提供了两种持久化方式:RDB(默认) 和AOF
RDB:
rdb是Redis DataBase缩写
功能核心函数rdbSave(生成RDB文件)和rdbLoad(从文件加载内存)两个函数
弊端:丢失数据相对多一些、时点与时点之间窗口数据容易丢失
优点:类似java中的序列化,恢复速度相对快。
AOF:
Aof是Append-only file缩写
每当执行服务器(定时)任务或者函数时flushAppendOnlyFile 函数都会被调用, 这个函数执行以下两个工作
aof写入保存:
WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件
SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。
存储结构:
内容是redis通讯协议(RESP )格式的命令文本存储。
优点:AOF丢失数据少
弊端:体量无限变大--->恢复慢
设计一个方案让日志,AOF足够小
1、hdfs,fsimage+edits.log 让日志只记录增量合并的过程
2、redis 4.0以前:重写-->删除抵消的命令,合并重复的命令-->最终也是一个纯指令的日志文件
3、redis 4.0以后:重写-->将老的数据RDB到AOF文件中将增量的以指令的方式Append到AOF--->AOF是一个混合体,利用了RDB的快,利用了日志的全量
redis是内存数据库:写操作会触发IO
redis配置文件
appendonly yes #开启
appendfilename "appendonly.aof"
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
appendfsync always
appendfsync everysec
appendfsync no
比较:
1、aof文件比rdb更新频率高,优先使用aof还原数据。
2、aof比rdb更安全也更大
3、rdb性能比aof好
4、如果两个都配了优先加载AOF
刚刚上面你有提到redis通讯协议(RESP ),能解释下什么是RESP?有什么特点?
RESP 是redis客户端和服务端之前使用的一种通讯协议;
RESP 的特点:实现简单、快速解析、可读性好
干我们这行,啥时候懈怠,就意味着长进的停止,长进的停止就意味着被淘汰,只能往前冲,直到凤凰涅槃的一天!