事务概念
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。这点和mysql的事务不太一样,在mysql事务开启后,当操作中存在异常,则会导致事务回滚,不会出现数据异常,但是对于redis而言,当事务执行过程中,出现异常,redis会
记录异常,但是操作会继续执行。
redis事务是一组命令的集合。多组命令进入到等待执行的事务队列中,执行exec命令告诉redis将等待执行的事务队列中的所有命令,按顺序执行,返回值就是这些命令组成的列表。
Redis 事务可以一次执行多个命令, 具有下列保证:
- 批量操作在发送 EXEC 命令前被放入队列缓存。
- 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
- 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历以下三个阶段:
- 开始事务。
- 命令入队。
- 执行事务。
事务中的错误:
- 事务在执行 EXEC 之前,入队的命令可能会出错。比如说,命令可能会产生语法错误(参数数量错误,参数名错误,等等),或者其他更严重的错误,比如内存不足(如果服务器使用 maxmemory 设置了最大内存限制的话)。
- 命令可能在 EXEC 调用之后失败。举个例子,事务中的命令可能处理了错误类型的键,比如将列表命令用在了字符串键上面,诸如此类。
服务器会对命令入队失败的情况进行记录,并在客户端调用 EXEC 命令时,拒绝执行并自动放弃这个事务
例如:
在 EXEC 命令执行之后所产生的错误, 并没有对它们进行特别处理: 即使事务中有某个/某些命令在执行时产生了错误, 事务中的其他命令仍然会继续执行
例如:
redis 事务入队只会检查语法错误,对于exec后执行错误,没有回滚措施。而且在事务中无法在客户端做查询判断,只会得到queued,无法进行业务数据判断,也是很坑。
redis的事务命令:
multi 开启事务
exec 执行
discard 取消事务
watch key 监视key
unwatch key取消监视
乐观锁
乐观的认为数据不会出现冲突,使用version或timestamp来记录判断。乐观锁的优点开销小,不会出现锁冲突。
可利用watch命令监听key,实现乐观锁,来保证不会出现冲突,应用场景比如秒杀来防止超卖。
伪代码如下:
实际代码如下:
//商品总量 $shop_account = $request->input('number'); //商品总库存的键 $products = 'products'; //判断商品是否存在 if(!Redis::exists($products)){ return $this->failed('购买的商品已下架'); } //判断商品是否还有库存 if(Redis::get($products) == 0){ return $this->failed('商品暂无库存,请联系店家上货'); } //判断购买的商品数量是否大于库存数量 if(Redis::get($products) < $shop_account){ return $this->failed('购买商品超出库存总量'); } //开始购买 try{ //监视库存总量 Redis::watch($products); //开启事务 Redis::multi(); //购买后减少库存 Redis::decrby($products,$shop_account); //下面肯定会有一些其余的操作... //结束,并执行事务 $res = Redis::exec(); //判断$products在事务执行过程中,是否被更改了 if ($res === false){ return $this->failed('店家更改了商品库存总量,请重新提交'); } return $this->message('购买成功,感谢您的支持'); }catch (Exception $exception){ //假如在事务入列中,存在异常,则取消事务 Redis::discard(); }