事务
Redis事务本质:一组命令的集合!一个事务中的所有名列都会被序列化,在事务执行的过程中,会按照顺序执行!
特性:一次性、顺序性(按照入队顺序执行)、排他性(在执行的过程中不会被其他客户端发送来的命令打断)、队列中的命令在事务没有被提交之前不会被实际执行
----------队列 命令1 命令2 命令3 执行----------
Redis事务没有隔离级别的概念!
所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行! exec
Redis单条命令保持原子性,但是事务部保证原子性!
redis的事务:
- 开启事务(multi)
- 命令入队(…)
- 执行事务(exec)
正常执行事务!
multi # 开启事务
set k1 1
set k2 2
set k3 3
get k1
set k4 4
exec # 执行职务
OK # 一次性执行队列中的命令
OK
OK
"1"
OK
放弃事务
discard # 开启事务后 输入该命令直接放弃本次事务 事务队列中命令都不会被执行!
事务出错的情况
-
命令有问题,事务中所有的命令都不会被执行!
如果开启事务后,将错误的命令入队,那么抛出异常,并且事务不会被执行
-
执行时出现错误,命令没有出错
multi set k1 1 incr k1 # 数字可以自增 set k2 a incr k2 # 字符串不可以自增 运行出错 但是命令没有错误 exec 1) OK 2) "1" 3) OK 4) (error) ERR value is not an integer or out of range # 出错的命令无法执行,但是不会影响其他入队的命令执行
乐观锁 监视Watch (当执行exec discard unwatch 都可以取消监控) watch key [key …] 可以监控多个key
set money 100
set out 10
watch money # 监视money对象
multi
decrby money 20
incrby out 20
exec # 事务正常结束,meoney在事务执行时(输入exec之前)没有变动,那么正常执行成功
1) (integer) 80
2) (integer) 20
多线程修改值,使用watch当做redis乐观锁
线程1 线程2
set money 100 get money
set out 10 decrby money 30
atch money # 监控
multi
decrby money 20
incrby out 20
exec
(nil) # 此时执行事务失败,因为另一个线程更改了正在被监控的money的值
发布订阅
订阅一个或者多个频道
subscribe(订阅) channel(频道) [channel....]
127.0.0.1:6379> subscribe a1 a2
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "a1"
3) (integer) 1
1) "subscribe"
2) "a2"
3) (integer) 2
给指定的频道发送消息
另外一个线程,发送信息
127.0.0.1:6379> publish a1 bb
(integer) 1
127.0.0.1:6379> publish a2 hello how are you
(error) ERR wrong number of arguments for 'publish' command
127.0.0.1:6379> publish a2 hellohaoareyou
(integer) 1
127.0.0.1:6379> PUBLISH c.ad 1 # 如果发送的频道没有被订阅,那么消息就不会发出
(integer) 0
订阅频道,接受信息 只会接受发过来的信息,不会接收谁发过来的
127.0.0.1:6379> publish a1 bb # 给频道a1发送消息bb
(integer) 1
127.0.0.1:6379> publish a2 hello how are you # 要加, hello,how,are,you
(error) ERR wrong number of arguments for 'publish' command
127.0.0.1:6379> publish a2 hellohaoareyou
(integer) 1
127.0.0.1:6379>
订阅指定模式的频道,*代表所有
psubscribe (subscribe)
127.0.0.1:6379> psubscribe a.* b1.* # 订阅的频道可以用于正则匹配,也就是订阅了以a.开头的频道,和b1.*开头的频道
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "a.*"
3) (integer) 1
1) "psubscribe"
2) "b1.*"
3) (integer) 2
发送信息
127.0.0.1:6379> PUBLISH a.asdad bb # 给a.asdad频道发送消息 你订阅的a.*频道也可以接受到
(integer) 1
接受信息
1) "pmessage"
2) "a*" # 那个频道的消息
3) "a.asdad" # 那个频道的消息
4) "bb" # 消息内容
发布订阅 使用子命令形式
pubsub subcommand(子命令) [argument(论证) [argument....]]
pubsub channels(通道) # 查看有多少活跃的频道
127.0.0.1:6379> subscribe a1 a2
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "a1"
3) (integer) 1
1) "subscribe"
2) "a2"
3) (integer) 2
127.0.0.1:6379> pubsub channels
1) "a1"
2) "a2"
# 注意此时,如果你是通过psubscribe订阅的那么就不算活跃的频道!!!
127.0.0.1:6379> psubscribe a1.* a2.*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "a1.*"
3) (integer) 1
1) "psubscribe"
2) "a2.*"
3) (integer) 2
127.0.0.1:6379> pubsub channels
(empty array)
pubsub numsub 频道 # 查看这个频道的订阅人数
127.0.0.1:6379> pubsub numsub a1 # 有两个线程订阅了a1这个频道
1) "a1"
2) (integer) 2
退订所有给定模式的频道
punsubscribe [pattern [pattern ...]]
# !!!!!!!!! 应用场景
punsubscribe命令不能应用于redis-cli客户端中,因为redis-cli运行订阅后,客户端处于阻塞模式,只能通过Ctrl-C退出订阅模式,而不可以通过punsubscribe命令进行相关操作。
当我们使用编程语言进行redis开发时,此时我们订阅相应的信道后,并且让订阅服务运行在后台运行,
当我们需要取消订阅时,就可以通过发送此命令取消当前客户端所订阅的信道或按模式匹配出相应的信道。
其他线程
127.0.0.1:6379> punsubscribe a1
1) "punsubscribe"
2) "a1"
3) (integer) 0
# 后台运行的redis,也就是发布订阅的这个redis-cli 因为是在后台运行的订阅,所以可以,如果不是在后台运行的话,输入该行命令无效
redis 127.0.0.1:6379> PUNSUBSCRIBE mychannel
1) "punsubscribe"
2) "a"
3) (integer) 1
redis的配置文件
grep -v '^$' redis.conf |grep -v '^#'
1、-v是grep排除的参数,例如查询除了包含ABC的行,例如cat a.txt|grep -v 'ABC'
2、^代表行首,$代表行尾。 ^$是空知行的意思
3、^#表示首字母为#的行,linux里的文件,#号开头一般都是注释内容
4、grep -v '^$' /etc/rsyslog.conf | grep -v '^#'
查询/etc/rsyslog.conf文件,但是不包含空行和注释行
bind 127.0.0.1 # 监听的地址
protected-mode yes # 将redis运行在安全模式下
port 6379 # redis的端口号
tcp-backlog 511
timeout 0
tcp-keepalive 300 # TCP的连接
daemonize yes # 是否以守护进程开始 开启后可以后台运行 vim编辑中 替换:%s/damonize no/daemonize yes/ # 替换为yes
supervised no
pidfile /var/run/redis_6379.pid # 更改这里和上面的port就可以启动新的redis服务端
loglevel notice # 日志的级别
logfile ""
databases 16 # 默认16个库
always-show-logo yes
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
rdb-del-sync-files no
dir ./
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-diskless-load disabled
repl-disable-tcp-nodelay no
replica-priority 100
acllog-max-len 128
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
lazyfree-lazy-user-del no
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
# requirepass 密码 将配置文件中的这行取消注释,下次登录client端的时候就要用 auth + 密码才可以进入
# redis-cli
- 1.配置文件unit单位 对大小写不敏感!
- 引入其他配置文件
就好像import
-
网络
binde 127.0.0.1 # 绑定的ip protected-mode yes # 保护模式 默认是开启的 port 6379 # 端口设置
-
守护进程
默认是no,我们需要自己开启为yes!
如果以后台的方式运行,那么我们就要指定一个后台文件
- 日志
- 默认数据库数量与是否显示LOGO
- 快照(持久化保存,rdb)
- 密码
-
也可以用命令行的形式:
config get requirepass # 获取redis的密码 前提是你要连接进去才行查看 127.0.0.1:6379> config get requirepass 1) "requirepass" 2) "123" config set requirepass 123456 # 设置redis密码 默认密码为空 auth 密码 # 使用密码登录,如果不登录是输入不了命令的
-
限制clients
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)
2、allkeys-lru : 删除lru算法的key
3、volatile-random:随机删除即将过期key
4、allkeys-random:随机删除
5、volatile-ttl : 删除即将过期的
6、noeviction : 永不过期,返回错误 # 默认
- append only模式 aof持久化存储配置
1.lsof -i:端口号 (通过端口号查看进行的pid(用于 kill结束后台运行的进程))
2.ps -ef|grep 名字(ps -ef|grep redis) 通过程序名字查看进行的pid 同样用于结束后台运行的进程
启动新的redis
grep -v '^$' redos.conf |grep -v '^#' >> redis6380.conf # 创建一个新文件里面是没有注释和空行的redis配置文件,在将里面的这些该了就可以
port 6380 # redis的端口号
pidfile /var/run/redis_6380.pid # 更改这里和上面的port就可以启动新的redis服务端
daemonize yes # 是否以守护进程开始 开启后可以后台运行 vim编辑中 替换:%s/damonize no/daemonize yes/ # 替换为yes
redis-server redis6380.conf # 以配置文件启动redis服务端
Redis持久化
面试和工作,持久化都是重点!
redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以redis提供了持久化的功能!
RDB
- 什么是RDB? (Rdis DataBase)
- 在主从复制中,rdb就是备用了!从机上面!
rdb持久化流程图
在指定的时间间隔内将内存中的数据集(快照)写入磁盘,也就是Snapshot快照,它恢复时是将快照文件直接读到内存里。
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个持久化过程中,主进程是不进行任何IO操作的。非常的高效。如果需要进行大规模数据的恢复,且对数据恢复的完整性不是非常的敏感,那么RDB方式要比AOF方式高效的多。注意:RDB最后一次持久化的数据很可能丢失。
生产环境中我们会将这个文件进行备份
save 900 1 # 在900秒以内有1次更新,就会持久化
save 300 10 # 300秒以内有10次更新,就会持久化
save 60 10000# 60秒以内有10000次的更新,就会持久化
这些可以同时存在
dbfilename dump.rdb # 数据保存的文件 在配置文件中 里面的内容都是二进制
触发机制
-
手动触发(save)rdb规则
-
执行flushall命令,也会触发rdb规则
-
退出redis,也会产生rdb文件
-
系统会设置默认的时间,用于自动生成二进制文件来进行数据持久化(一段时间,进行多少次操作进行保存)
使用rdb文件恢复数据
- 只需要将rdb文件放入redis启动目录就可以,redis启动的时候会自动检查dump.rdb恢复其中的数据!
-
查看rdb文件位置
config get dir 127.0.0.1:6379> config get dir 1) "dir" 2) "/usr/local/bin" # 如果在这个目录下存在dump.rdb文件,启动redis就会自动恢复区中的数据
-
优点:
- rdb可以用作备份
- 比较适合做灾难恢复
- redis是多路复用的,主进程会fork一个子进程出来,子进程用来复制保存数据
-
缺点:
- 如果说数据需要每次操作每次数据都保存下来,也就是把数据每次都全部保存下来,rdb做不到(比如 300秒内有10次操作就会存盘,但是你如果操作了9次,然后redis崩了,这些数据就不会被保存下来,并且你也不会每次都save一下 )
- fork进行的时候,会占用一定内存空间
- 如果redis意外宕机了,那么这个最后一次修改的数据就没有了
AOF
什么是aof(append only file)
将我们写入,删除等对数据库进行修改的命令记录下来,恢复的时候就把这个文件全部在执行一遍(该文件不是以二进制存储的)
aof流程图
以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读等不对数据库进行数据改变的操作不记录),只允许追加文件单不可以改写文件,redis启动之初会读取该文件重新构建数据,也就是redis重启的话就根据日志文件(aof)(一般rdb为备份文件)的的内容将写命令从前到后执行一次以完成数据恢复的工作
开启后注意重新启动服务端
如果aof文件出现错误,这个时候redis是启动不起来的,
此时我在重新启动服务端,连接数据库
[root@chuxin bin]# ps -ef|grep redis
root 18974 17410 0 15:31 pts/2 00:00:13 redis-server 127.0.0.1:6380
root 20064 1 0 18:32 ? 00:00:00 redis-server 127.0.0.1:6379
root 20111 17245 0 18:38 pts/1 00:00:00 grep --color=auto redis
[root@chuxin bin]# kill -9 20064
[root@chuxin bin]# redis-server cp_redis.conf/redis.conf
20113:C 19 May 2020 18:38:46.963 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
20113:C 19 May 2020 18:38:46.963 # Redis version=6.0.1, bits=64, commit=00000000, modified=0, pid=20113, just started
20113:C 19 May 2020 18:38:46.963 # Configuration loaded # 加载配置文件
[root@chuxin bin]# redis-cli
Could not connect to Redis at 127.0.0.1:6379: Connection refused # 加载失败 因为appendonly.aof 中有错误行
not connected>
接下来我们就要用redis给我们提供的工具 redis-check-aof --fix进行修复日志文件
redis-check-aof --fix appendonly.aof
0x 68: Expected \r\n, got: 7364
AOF analyzed: size=128, ok_up_to=79, diff=49
This will shrink the AOF from 128 bytes, with 49 bytes, to 79 bytes
Continue? [y/N]: y
Successfully truncated AOF
# 进行修复 注意 此时修复的话,会将错误的行删除 保留正确的行,比如上面的set a3 3就被删除了
# 修复成功后就可以,启动服务端了
127.0.0.1:6379> keys *
1) "a2"
2) "a1"
重写规则
优点:
- 每一次修改同步,文件的完整性会更好!
- 每秒同步一次,增加容错率,只可能会丢失一秒的数据
缺点:
- 相对于数据数据文件来说,aof远远大于rdb,修复的速度也比rdb慢!
- aof运行效率也要比rdb慢,所以redis默认的配置就是rdb持久化!并且不开启aof
总结
1、RDB 持久化方式能够在指定的时间间隔内对你的数据进行快照存储
2、AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以Redis 协议追加保存每次写的操作到文件末尾,Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。
3、只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化
4、同时开启两种持久化方式
- 在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据, 因为在通常情况下AOF 文件保存的数据集要比RDB文件保存的数据集要完整。
- RDB 的数据不实时,同时使用两者时服务器重启也只会找AOF文件,那要不要 只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变 化不好备份),快速重启,而且不会有AOF可能潜在的Bug,留着作为一个万一 的手段。
5、性能建议
- 因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留 save 900 1 这条规则。
- 如果Enable AOF ,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价一是带来了持续的IO,二是AOF rewrite 的最后将 rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite 的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上,默认超过原大小100%大小重写可以改到适当的数值。
- 如果不使用AOF ,仅靠 Master-Slave Repllcation 实现高可用性也可以,能省掉一大笔IO,也减少了rewrite时带来的系统波动。代价是如果Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个 Master/Slave 中的 RDB文件,载入较新的那个,微博就是这种架构。
用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变 化不好备份),快速重启,而且不会有AOF可能潜在的Bug,留着作为一个万一 的手段。
5、性能建议
- 因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留 save 900 1 这条规则。
- 如果Enable AOF ,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价一是带来了持续的IO,二是AOF rewrite 的最后将 rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite 的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上,默认超过原大小100%大小重写可以改到适当的数值。
- 如果不使用AOF ,仅靠 Master-Slave Repllcation 实现高可用性也可以,能省掉一大笔IO,也减少了rewrite时带来的系统波动。代价是如果Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个 Master/Slave 中的 RDB文件,载入较新的那个,微博就是这种架构。