当前位置: 首页 > 科技观察

Redis缓存技术学习系列之事务处理_0

时间:2023-03-12 09:01:06 科技观察

在本系列的第一篇文章中,我们主要学习了Redis中的“键”和“值”。我们可以注意到Redis是一个C/S架构的数据库。在我们目前的认知中,它是通过终端中的命令进行存储和读取的,也就是非常典型的“请求-响应”模型。但是我们知道,在实际应用中,我们可能要面对更复杂的业务逻辑,因为传统关系型数据库中表的概念在Redis中是不存在的,所以在使用Redis的过程中,我们不得不面对两个实际问题,即如何更好地维护数据库中的“密钥”,如何在高效执行命令的同时保证命令的成功执行。对于前者,我认为是设计问题,对于后者,我认为是技术问题。因此,本文的核心内容就是寻找这两个问题的答案。带着这样一个问题,我们可以正式进入本文的主题:Redis中的事务处理。从数据库事务说起通常我们提到数据库就免不了提到事务,那么什么是事务呢?事务是指作为单个逻辑工作单元执行的一系列操作。所以,首先一个事务就是一系列的操作,而这一系列的操作有一个二态性,即完全执行或者根本不执行。事务处理因此确保面向数据的资源不会更新,除非事务单元内的所有操作都成功完成。让我们在这里举个例子。除了数据库中的查询操作外,插入(Insert)、删除(Delete)和更新(Update)这三种操作都会对数据产生影响,因为事务处理可以保证一系列操作可以完全执行或不执行根本没有,所以在一个事务提交之后,当事务中的任何一条SQL语句被执行时,都会产生一个撤销日志(UndoLog),撤销日志中的记录是与当前操作完全相反的操作,比如与删除相反的是插入,与插入相反的是删除等等,我们通常所说的事务回滚,其实就是在这些plug日志中进行相反的操作,这也告诉我们一个道理,事务中只有一系列的操作才能被当它们完全执行时回滚。事务中的一系列操作没有完全执行。这个时候,我们不能保证数据可以回滚。在数据库相关理论中,一个逻辑工作单元要想成为一个事务,必须满足ACID,即原子性、一致性、隔离性、持久性.(1):原子性的概念其实就是一个事务中所有的SQL操作都是一个整体,所以只有所有的SQL操作都完全执行成功了,事务方才能认为commit成功。如果在事务提交过程中某条SQL语句执行失败,则必须将整个事务回滚到事务提交前的状态。(2):一致性的概念是指当一个事务完成后,所有的数据都必须保持一致的状态,数据库各个组件的实现需要开发者保证数据、索引、约束、日志等.交易前后具有一致性。(3):隔离的概念主要是针对并发的。其核心思想是不同并发事务对数据的修改必须相互隔离。假设有两个不同的事务A和B同时执行,那么对于A来说,它在执行前只有两个状态,即B执行前和B执行后。同理,B也是如此,我们称之为特征隔离。(4):持久化比较简单,就是事务完成后,它对数据的影响是永久的。Redis中的事务处理好了,至此,我们对数据库中事务处理的相关理论有了一个基本的了解。或许这个世界上的数据库系统千差万别,但我相信它们最终都会解决事务处理的问题。会殊途同归,就像我们解决并发过程中的冲突问题,常规的方法还是加锁。这就是我花精力去理解和解释这些理论知识的原因。技术日新月异。如果我们总是盲目地为新技术而努力,那么也许我们会逐渐失去对这个行业的热爱。我相信原则永远比框架更重要。我没有系统地学过计算机课程。这件事让我很遗憾。.Redis中的事务可以看作是一个队列,即我们可以通过MULTI来启动一个事务,相当于声明了一个命令队列。接下来,我们提交给Redis的每个命令都会被排队到这个命令队列中。当我们输入EXEC命令时,会触发当前事务,相当于从命令队列中取出命令并执行,所以Redis中一个事务会经历三个阶段:启动事务、命令入队、执行事务开始执行。下面是一个在Redis中使用事务的简单例子:127.0.0.1:6379>MULTIOK127.0.0.1:6379>SETBook_Name"GItPro"QUEUED127.0.0.1:6379>SADDProgram_Language"C++""C#""Jave""Python"QUEUED127.0.0.1:6379>GETBook_NameQUEUED127.0.0.1:6379>EXEC1)OK2)(integer)43)"GItPro"我们可以注意到Redis中的事务和通常意义上的事务基本一致,即一个事务是一个事务由一系列操作组成的单个逻辑工作执行单元。特别地,由于命令在Redis中是存储在队列中的,所以一个事务中的所有命令都会顺序执行,不会在事务执行过程中被客户端发送的其他命令打断。事务是一个原子操作,事务中的命令只有两种执行结果,即全部执行或完全不执行。如果客户端用MULTI命令打开事务后,由于意外没有执行EXEC命令,则事务中的所有命令都不会执行。同理,如果客户端在使用MULTI命令开启事务后执行EXEC命令,则该事务中的所有命令都会被执行。Redis中的事务可以使用DISCARD命令来清空一个命令队列,放弃事务的执行。如果在命令入队时发生错误,Redis会在客户端调用EXEC命令时拒绝执行并取消事务,但如果在执行EXEC命令后发生错误,Redis会选择自动忽略。我们知道常见的并发控制方案主要有悲观锁和乐观锁。这里我们先解释一下这两个概念。所谓悲观锁,顾名思义,就是一种悲观策略。悲观锁认为:在任何记录被修改之前,都应该加锁。如果锁失败,说明记录正在被修改,此时应该抛出异常;如果加锁成功,事务完成后修改记录并解锁;如果其他人修改了它,它应该等待当前修改被解锁或者抛出异常。所谓乐观锁,顾名思义,就是一种乐观策略。乐观锁认为:每次从记录中查找数据,别人是不会修改的,所以这个过程中不需要加锁,但是在更新记录的时候,会通过版本号来判断别人是否有修改了当前记录。一般来说,乐观锁适合写冲突比较少的场合,悲观锁适合写冲突比较多的场合。Redis提供了一种叫做check-and-set的机制,主要是通过WATCH命令来实现的。它的原理是基于乐观锁策略。Redis在执行EXEC命令之前,会检查被监控的key对应的值。是否有变化,如果值发生变化,说明有人修改了这个key存储的值,此时Redis会自动取消当前事务。让我们看一下这个简单的例子:WATCHRecord_Countval=GETRecord_Countval=val+1MULTISETRecord_Count$valEXEC在这个例子中,我们尝试在事务中对Record_Count执行自增操作。这段代码在非并发的情况下是没问题的,但是在并发的情况下,如果用户在执行EXEC命令前修改了Record_Count的值,那么此时我们的结果就会比预期的结果小1。现在有了WATCH,Redis会监控Record_Count。当Redis检测到值发生变化时,会自动取消事务,避免冲突。Rediskeys如何管理其实从题目来看,这篇博客基本把事务处理问题说清楚了,所以这篇博客虽然没有给大家带来很多惊喜,但是还是可以很好的结束这个话题,但是因为之前,有朋友在博客中留言询问Redis的key管理,所以博主决定在这里简单讨论一下这个问题。由于博主和大家一样都是Redis新手,所以以下观点仅代表一家之言,有什么问题可以在博客里留言,欢迎大家批评指正。我认为Redis中对key的管理基本上有两种策略,即惰性删除和定时删除。其实这是Redis默认的key删除策略:redis使用惰性删除和定时删除两种策略来删除过期的Key:惰性删除策略只有遇到过期的key才删除,周期性删除策略主动查找和删除定期删除过期的密钥。因此,基于这两种key删除策略,我们可以想到如下方法:可以使用临时key来存放临时变量,在数据库中全局设置一个过期时间,Redis会在key过期后自动删除。对于持久化数据,可以使用普通的key来存储,客户端可以通过服务端和客户端之间定义的协议主动删除key。对于不同模块中的键,采用统一规范的命名规则来命名键,解决Redis中键管理混乱的问题。设计合理的key回收机制,防止Redis使用超过95%的内存,或者通过在Redis中设置最大内存容量和内存策略,主动触发Rediskey的淘汰。好了,本文就到此为止,希望大家喜欢,我们下篇见!