redis实战之事务与持久化
1. 事务描述
(1)什么是事务
事务,就是把一堆事情绑在一起,按顺序的执行,都成功了才算完成,否则恢复之前的样子
事务必须服从ACID原则,ACID原则分别是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)
原子性:操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态
一致性:事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定
隔离性:在该事务执行的过程中,无论发生的任何数据的改变都应该只存在于该事务之中,对外界不存在影响,只有在事务确认提交之后们才会显示该事务对数据的改变,其他事务才能获取到这些改变后的数据
持久性:当事务正确完成后,它对于数据的改变是永久性的
(2)并发事务导致的常见错误
第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖
脏读:一个事务读取到另一个事务未提交的更新数据
幻读也叫虚读:一个事务执行两次查询,第二次结果集包含第一次中没有或某些行已经被删除的数据,造成两次结果不一致,只是另一个事务在这两次查询中间插入或删除了数据造成的
不可重复读:一个事务两次读取同一行的数据,结果得到不同状态的结果,中间正好另一个事务更新了该数据,两次结果相异,不可被信任
第二类丢失更新:是不可重复读的特殊情况。如果两个事物都读取同一行,然后两个都进行写操作,并提交,第一个事物所做的改变就会丢失
2. redis事务处理
redis事务通过MULTI、WATCH、UNWAYCH、EXEC、DISCARD五个命令实现
MUTIL命令:用于开启一个事务,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行,它总是返回OK
WATCH命令:对键进行监视,直到用户执行EXEC命令的这段时间里面,如果其他客户端抢先对任何被监视的键进行了替换、更新或删除等操作,那么当用户舱室执行EXEC命令的时候,事务将失败并返回一个错误(之后用户可以选择重试或者放弃事务)
UNWATCH命令:在WATCH命令执行之后,EXEC命令执行之前对链接进行重置
EXEC命令:执行事务命令
DISCARD命令:客户端可以取消WATCH命令名清空所有已入队命令
3. redis事务示例
下面将用商品交易示例说明事务处理过程
(1)将商品投放到市场
a. 使用散列(users:)来管理市场中的所有用户信息,包括用户名、用户拥有的钱
b. 使用集合(inventory:)来管理每个用户的所有商品信息,包括商品名名
inventory:1对应用户User:1的报告
c. 使用有序集合(market:)来管理投放到市场中的商品
流程:检查inventory:1包裹中是否含有ItemL商品----->将inventory:1包裹中的ItemL商品添加到交易市场----->删除inventory:1包裹中的ItemL商品
def list_item(conn,userid,goodsname,price):
#使用有序集合(market:)来管理投放到市场中的商品
#商品的key为userid:goodsid
inventory = 'inventory:%s' %(userid)
user = 'User:%s' %(userid)
goodsitem = '%s:%s' %(userid,goodsname)
end = time.time() +
pipe = conn.pipeline()
while time.time() < end:
try:
pipe.watch(inventory) #监视包裹发生的变化
if not pipe.sismember(inventory,goodsname): #检查用户是否仍然持有将要被放入市场的商品
pipe.unwatch()
return None
pipe.multi()
pipe.zadd('market:',{goodsitem:price}) #将商品投放到市场
pipe.srem(inventory, goodsname) #从用户包裹中删除该商品
pipe.execute()
print('商场中的商品:',conn.zrange('market:', , -,withscores=True))
print('商品投放市场后用户{}的商品:{}'.format(userid, conn.smembers(inventory)))
return True
except redis.exceptions.WatchError as e:
print(e)
return False
测试:
(2)交易:User:2购买交易市场中的中的ItemL商品
思想:使用watch对市场以及买家的个人信息进行监视,然后获取买家拥有的钱数以及商品市场的售价,并检查买家是否有足够的钱来购买商品,如果买家没有足够的钱,那么程序会取消事务,如果买家钱足够,那么程序首先会将买家支付的钱转移给卖家,然后将售出的商品移除商品交易市场。
def purchase_item(conn, buyerid, goodsname, sellerid):
buyer = 'User:{}'.format(buyerid) #买家key
seller = 'User:{}'.format(sellerid) #卖家key
buy_inventory = 'inventory:{}'.format(buyerid) #买家包裹key
sell_inverntory = 'inventory:{}'.format(sellerid) #卖家包裹key
goodsitem = '{}:{}'.format(sellerid,goodsname) #市场中的商品key end = time.time() +
pipe = conn.pipeline()
while time.time() < end:
try:
pipe.watch('market:',buyer) #对商品买卖市场以及买家的个人信息进行监视
funds = int(pipe.hget(buyer,'funds')) #得到买家拥有的钱
print('买家手上的钱:', funds)
price = pipe.zscore('market:', goodsitem) #得到商品的价格
print('price:',price)
if funds < price:
pipe.unwatch()
return None pipe.multi()
pipe.hincrby(buyer, 'funds', int(-price)) #买家的钱减少
pipe.hincrby(seller, 'funds', int(price)) #卖家的钱增加
pipe.sadd(buy_inventory,goodsname)
pipe.zrem('market:', goodsitem)
pipe.execute()
print('交易市场中的商品:', conn.zrange('market:', , -, withscores=True))
print('买家现在手中的钱:',conn.hget(buyer, 'funds'))
print('买家包裹内容:', conn.smembers(buy_inventory))
print('卖家现在手中的钱:',conn.hget(seller, 'funds'))
return True
except redis.exceptions.WatchError as e:
print(e)
return False
测试:
if __name__ == '__main__':
conn = redis.Redis()
#create_users(conn)
#create_user_inventory(conn)
list_item(conn,,'ItemL',)
purchase_item(conn, , 'ItemL', )
4. redis持久化
redis提供了两种不同的持久化方式来将数据从内存存储到硬盘里面,一种是RDB持久化,原理是将redis内存中的数据库记录定时dump到磁盘上的RDB持久化,另外一种是AOF(append only file)持久化,原理是被执行的写命令复制到磁盘里。
(1)RDB持久化配置
# Save the DB on disk:
# 设置sedis进行数据库镜像的频率。
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)。
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)。
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)。
save
save
save stop-writes-on-bgsave-error yes
# 在进行镜像备份时,是否进行压缩。yes:压缩,但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间。
rdbcompression yes
# 一个CRC64的校验就被放在了文件末尾,当存储或者加载rbd文件的时候会有一个10%左右的性能下降,为了达到性能的最大化,你可以关掉这个配置项。
rdbchecksum yes
# 快照的文件名
dbfilename dump.rdb
# 存放快照的目录
dir ./
(2)创建快照的方法
a. 客户端可以通过想redis发送besave命令来创建一个快照,对于支持bgsave命令的平台来说(基本上所有平台都支持,除了windows平台),redis会调用fork来创建一个子进程,然后子进程负责将快照写入磁盘,而父进程继续处理命令请求
b. 客户端还可以向redis发送save命令来创建一个快照,接到save命令的redis服务器在快照创建完毕之前不再响应任何其他命令,save命令不常用,我们通常只会在没有足够内存去支持bgsave的情况下,又或者即使等待持久化操作执行完毕也无所谓的情况下,才会使用这个命令
c. 当redis通过shutdown命令接收到关闭服务器请求时,或者接收到标准term命令时,会执行一个save命令,阻塞所有客户端,不再执行客户端发送的任何命令,并在save命令执行完毕之后关闭服务器
d. 当一个redis服务器链接另一个redis服务器,并向对方发送sync同步命令来开始一次复制操作时,如果主服务器目前没有执行bgsave操作,或者主服务器并非刚刚执行完bgsave操作,那么主服务器就会执行bgsave
(3)AOF持久化配置
# 是否开启AOF,默认关闭(no)
appendonly yes
# 指定 AOF 文件名
appendfilename appendonly.aof
# Redis支持三种不同的刷写模式:
# appendfsync always #每次收到写命令就立即强制写入磁盘,是最有保证的完全的持久化,但速度也是最慢的,一般不推荐使用。
appendfsync everysec #每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,是受推荐的方式。
# appendfsync no #完全依赖OS的写入,一般为30秒左右一次,性能最好但是持久化最没有保证,不被推荐。 #在日志重写时,不进行命令追加操作,而只是将其放在缓冲区里,避免与命令的追加造成DISK IO上的冲突。
#设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes
no-appendfsync-on-rewrite yes
#当前AOF文件大小是上次日志重写得到AOF文件大小的二倍时,自动启动新的日志重写过程。
auto-aof-rewrite-percentage
#当前AOF文件启动新的日志重写过程的最小值,避免刚刚启动Reids时由于文件尺寸较小导致频繁的重写。
auto-aof-rewrite-min-size 64mb
(4)AOF重写原理
AOF 重写和 RDB 创建快照一样,都巧妙地利用了写时复制机制:
a. Redis 执行 fork() ,现在同时拥有父进程和子进程
b. 子进程开始将新 AOF 文件的内容写入到临时文件
c . 对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的
d. 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾
注意:在将内存缓存中的数据追加到新AOF文件末尾和rename时,主进程是阻塞的
redis实战之事务与持久化的更多相关文章
- Redis高级特性---------事务与持久化与发布订阅
一.redis事务的用法 1.开启事务:multi 2.提交事务:exec ( queued只是把指令放入队列中,没有执行) 3.取消事务:discard 4.redis事务不能保证同时成功或者失 ...
- Redis实战总结-配置、持久化、复制
Redis的配置主要放置在redis.conf,可以通过修改配置文件实现Redis许多特性,比如复制,持久化,集群等. redis.conf部分配置详解 # 启动redis,显示加载配置redis.c ...
- Redis实战 - 5事务:multi、exec和watch
介绍 redis的目标的是: 简洁,高效,由于事务本身就是一个很复杂的东西,所有我们不能把事务做的太复杂. DISCARD 取消事务,放弃执行事务块内的所有命令. EXEC 执行所有事务块内的命令. ...
- Redis实战总结-Redis的高可用性
在之前的博客<Redis实战总结-配置.持久化.复制>给出了一种Redis主从复制机制,简单地实现了Redis高可用.然后,如果Master服务器宕机,会导致整个Redis瘫痪,这种方式的 ...
- .Net Redis实战——事务和数据持久化
Redis事务 Redis事务可以让一个客户端在不被其他客户端打断的情况下执行多个命令,和关系数据库那种可以在执行的过程中进行回滚(rollback)的事务不同,在Redis里面,被MULTI命令和E ...
- Redis数据库 02事务| 持久化| 主从复制| 集群
1. Redis事务 Redis不支持事务,此事务不是关系型数据库中的事务: Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化.按顺序地执行.事务在执行的过程中,不会被其他客户端发送来的 ...
- redis 事务,持久化,日志,主从,VM
redis目前对事务的支持比较简单,只能保证一个客户端连接发起事务中的命令可以连续执行,而中间不会插入其他客户端的命令. 1.事务 一般情况下,redis接收到一个客户端发送的命令,立刻执行并返回结果 ...
- Redis事务,持久化,哨兵机制
1 Redis事务 基本事务指令 Redis提供了一定的事务支持,可以保证一组操作原子执行不被打断,但是如果执行中出现错误,事务不能回滚,Redis未提供回滚支持. multi 开启事务 exec 执 ...
- Redis实战 | 持久化、主从复制特性和故障处理思路
前言 前面两篇我们了解了Redis的安装.Redis最常用的5种数据类型.本篇总结下Redis的持久化.主从复制特性,以及Redis服务挂了之后的一些处理思路. 前期回顾传送门: Linux下安装Re ...
随机推荐
- 备注字段长度控制JS
//变更原因备注字符长度控制 function checkChangeLength() { var field = $("#changeReasonDesc").val(); ma ...
- php 跨域 form提交 2种方法
出于安全因素考虑,直接跨域访问是不允许的,下面介绍二种跨域的方法. 一,通过php curl function curlPost($url,$params) { $postData = ''; for ...
- JSP笔记03——环境搭建(转)
不完全翻译,结合谷歌,一定主观性,还可能有误,原始内容地址:https://www.tutorialspoint.com/jsp/jsp_environment_setup.htm [注释]这篇貌似有 ...
- Android系统--输入系统(三)必备Linux知识_双向通信(scoketpair)
Android系统--输入系统(三)必备Linux知识_双向通信(scoketpair) 引入 1. 进程和APP通信 创建进程 读取.分发 - 进程发送输入事件给APP 进程读取APP回应的事件 输 ...
- RazorEngine性能研究(反射的延深)
先说下结论 1)RazorEngine 确实很慢,编译过程特别慢,编译过后仍不适合大量重复调用的情况(一次调用可以接受). 2 ) RazorEngine 和 asp.net mvc 里的Razo ...
- Android中设置自己软件的铃声+震动
有时候一些通讯软件需要这些个功能,比如说收到短信,通知等,要求手机发出铃声,或震动,或发光以提示用户知晓. 往往手机都是有默认设置的,比如说用户开启了铃声+震动:只铃声不震动:完全静音等等... 这个 ...
- Start and Use the Database Engine Tuning Advisor
https://docs.microsoft.com/en-us/sql/relational-databases/performance/start-and-use-the-database-eng ...
- 【atcoder】All Your Paths are Different Lengths[arc102D](乱搞)
题目传送门:https://arc102.contest.atcoder.jp/tasks/arc102_b 这道题有点毒瘤啊,罚时上天.. 显然若$ l=2^n $那么就可以直接二进制拆分,但是如果 ...
- 【bzoj3747】Kinoman[POI2015](线段树)
题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3747 对于这种题,考虑固定区间的右端点为r,设区间左端点为l能取得的好看值总和为a[l] ...
- review17
关于构造方法Random(long seed)的理解 无参构造方法使用的默认参数是系统当前的毫秒数.使用同一数值的种子参数,生成的随机数也是一样的. 代码如下所示: import java.util. ...