目录
1.2 事务的相关命令-Multi、Exec、discard
一、事务
1.1 定义
Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis事务的主要作用就是串联多个命令防止别的命令插队。
1.2 事务的相关命令-Multi、Exec、discard
从输入Multi命令后开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。组队的过程中可以通过discard来放弃组队。
1.3 事务的错误处理
若在组队过程中某个命令出现了错误,则执行时整个队列都会被取消。
若在执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。
1.4 Redis事务的特性
- 单独的隔离操作
- 事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 没有隔离级别的概念
- 队列中命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
- 不保证原子性
- 事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
1.4 悲观锁
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。使用这样的锁效率较低。
1.5 乐观锁
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
1.6 使用乐观锁-WATCH、UNWATCH
在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
# 监视 key ,且事务成功执行
redis> WATCH lock lock_times
OK
redis> MULTI
OK
redis> SET lock "A"
QUEUED
redis> INCR lock_times
QUEUED
redis> EXEC
1) OK
2) (integer) 1
# 监视 key ,且事务被打断
redis> WATCH lock lock_times
OK
redis> MULTI
OK
redis> SET lock "B" # 就在这时,另一个客户端修改了 lock_times 的值
QUEUED
redis> INCR lock_times
QUEUED
redis> EXEC # 因为 lock_times 被修改, B的事务执行失败
(nil)
unwatch可以取消 WATCH 命令对所有 key 的监视。
如果在执行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被执行了的话,那么就不需要再执行UNWATCH 了。
1.7 关于高并发的抢票问题
给库存数据加上乐观锁(watch)可以解决抢票中的超卖问题(余量-1)。
通过连接池可以解决连接超时的问题。
通过LUA脚本可以解决库存遗留问题(秒杀之后竟然还有很多余量),实际上是redis 利用其单线程的特性,用任务队列的方式解决多任务并发问题。
二、持久化
RDB与AOF用哪个?
可以都用;
如果对数据不敏感,可以单独用RDB。不建议单独用AOF;
如果只是做纯内存缓存,可以都不用。
2.1 RDB(Redis DataBase)
在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里.
2.1.1 持久化流程
Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。 整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。持久化产生的文件默认为dump.rdb
如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
需要恢复数据时,只需把备份的文件拷贝到工作目录下,然后启动Redis, 备份数据会自动加载。
2.1.2 优缺点
优势:
- 适合大规模的数据恢复
- 对数据完整性和一致性要求不高更适合使用
- 节省磁盘空间
- 恢复速度快
劣势:
- Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑
- 虽然Redis在fork时使用了写时复制技术,但是如果数据庞大时还是比较消耗性能。
- 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
2.2 AOF(Append Of File)
AOF以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
AOF默认不开启,AOF和RDB同时开启,系统默认取AOF的数据
2.2.1 持久化流程
(1)客户端的请求写命令会被append追加到AOF缓冲区内;
(2)AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作同步到磁盘的AOF文件中;
同步频率:
always
始终同步,每次Redis的写入都会立刻记入日志;性能较差但数据完整性比较好
everysec
每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。
no
redis不主动进行同步,把同步时机交给操作系统。
(3)AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;
AOF文件持续增长而过大时,会fork出一条新进程来将文件重写。重写就是创建一个新的AOF文件来替代现有的AOF文件,新旧两个文件所保存的数据库状态是相同的,但是新的AOF文件不会包含任何浪费空间的冗余命令(合并命令),通常体积会较旧AOF文件小很多。
Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发
重写的流程:
(1)主进程fork出子进程执行重写操作,保证主进程不会阻塞。
(2)子进程遍历redis内存中数据到临时文件,客户端的写请求同时写入aof_buf缓冲区和aof_rewrite_buf重写缓冲区保证原AOF文件完整以及新AOF文件生成期间的新的数据修改动作不会丢失。
(3)1).子进程写完新的AOF文件后,向主进程发信号,更新统计信息。
2).主进程把重写缓冲区中的数据写入到新的AOF文件。
(4)使用新的AOF文件覆盖旧的AOF文件,完成AOF重写。
(4)Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的;
AOF的备份机制和性能虽然和RDB不同, 但是备份和恢复的操作同RDB一样,都是拷贝备份文件,需要恢复时再拷贝到Redis工作目录下,启动系统自动加载。
2.2.2 优缺点
优势:
- 备份机制更稳健,丢失数据概率更低。
- 可读的日志文本,通过操作AOF稳健,可以处理误操作。
劣势:
- 比起RDB占用更多的磁盘空间。
- 恢复备份速度要慢。
- 每次读写都同步的话,有一定的性能压力。
- 存在个别Bug,造成恢复失败。