Redis05: 事务处理实践

Redis事务简介

概述

Redis采用了乐观锁方式进行事务控制,它使用watch命令见识给定的key,当exec(t提交事务)的时候,如果监控的key是对整个连接有效的,比如断开连接,监事和事务都会被自动清除,当然,exec,discard,unwatch命令都会清除连接中的所有监视

基本指令

multi开启事务

exec提交事务

discard取消事务

watch 监控,如果监控的值发生变化,那么事务的提交会失败

unwatch取消监控

Redis保证一个事务中的所有命令要么都执行,要么都不执行(原子性)。如果在发送EXEC命令前客户端断线了,则Redis会清空事务队列,事务中的所有命令都不会执行。而一旦客户端发送了EXEC命令,所有的命令就都会被执行,即使此后客户端断线也没关系,因为Redis中已经记录了所有要执行的命令。

Redis事务控制实践

exec提交事务

模拟转账,tony 500,jack 200,tony转给jack100。

127.0.0.1:6379> set tony 500
OK
127.0.0.1:6379> set jack 200
OK
127.0.0.1:6379> mget tony jack
1) "500"
2) "200"
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379(TX)> decrby tony 100 #所有指令操作会进入到队列
QUEUED
127.0.0.1:6379(TX)> incrby jack 100
QUEUED
127.0.0.1:6379(TX)> mget tony jack
QUEUED 
127.0.0.1:6379(TX)> exec  #提交事务
1) (integer) 400
2) (integer) 300
3) 1) "400"
   2) "300"
127.0.0.1:6379> mget tony jack
1) "400"
2) "300"
127.0.0.1:6379>

discard取消事务

注意redis事务太简单,没有回滚,而只有取消。

127.0.0.1:6379> mget tony jack
1) "400"
2) "300"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby jack 100
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get jack
"300"
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI

当出现错误指令时,事务也会自动取消。

127.0.0.1:6379> mget tony jack
1) "400"
2) "300"
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incrby jack 100
QUEUED
127.0.0.1:6379(TX)> abcd
(error) ERR unknown command `abcd`, with args beginning with:
127.0.0.1:6379(TX)> get jack
QUEUED
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get jack
"300"
127.0.0.1:6379>

秒杀抢票事务处理

基于一个秒杀,抢购案例,演示redis乐观锁方式,例如

第一步:打开客户端1,执行如下操作

127.0.0.1:6379> set ticket 1
OK
127.0.0.1:6379> set money 0
OK
127.0.0.1:6379> watch ticket		#乐观锁,对值进行观察,改变则事务失败
OK
127.0.0.1:6379> multi				#开启事务
OK
127.0.0.1:6379> decr ticket
QUEUED
127.0.0.1:6379> incrby money 100
QUEUED

第二步:打开客户端2,执行如下操作,演示还没等客户端1提交事务,此时客户端2把票买到了

127.0.0.1:6379> get ticket
"1"
127.0.0.1:6379> decr ticket
(integer) 0

第三步,回到客户端1:提交事务,检查ticket的值

127.0.0.1:6379> exec
(nil) #执行事务,失败
127.0.0.1:6379> get ticket
“0”
127.0.0.1:6379> unwatch #取消监控

Jedis 客户端事务操作

基于Jedis进行事务测试,代码如下:

package com.jt;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class JedisTransactionTests {

    @Test
    public void testTransaction(){
        Jedis jedis=new Jedis("192.168.126.130",6379);
        jedis.auth("123456");
        jedis.set("tony","300");
        jedis.set("jack","500");
        //实现操作,tony转账100给jack
        //开启事务
        Transaction multi = jedis.multi();
        //执行业务操作
        try {
            multi.decrBy("tony", 100);
            multi.incrBy("jack", 100);
            int n=100/0;//模拟异常
            //提交事务
            multi.exec();
        }catch(Exception e) {
            //出现异常取消事务
            multi.discard();
        }
        String tonyMoney=jedis.get("tony");
        String jackMoney=jedis.get("jack");
        System.out.println("tonyMoney="+tonyMoney);
        System.out.println("jackMoney="+jackMoney);
        jedis.close();
    }
}

Jedis 客户端秒杀操作实践

package com.jt.demos;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Response;
import redis.clients.jedis.Transaction;

import java.util.List;

/**
 * redis秒杀练习:
 * 模拟两个线程都去抢购同一张票(考虑乐关锁)
 */
public class SecondKillDemo02 {

      public static void secKill(){
          Jedis jedis=new Jedis("192.168.126.130",6379);
          jedis.auth("123456");
          jedis.watch("ticket","money");
          String ticket = jedis.get("ticket");
          if(ticket==null||Integer.valueOf(ticket)==0)
              throw new RuntimeException("已无库存");
          Transaction multi = jedis.multi();
          try {
              multi.decr("ticket");
              multi.incrBy("money", 100);
              List<Object> exec = multi.exec();
              System.out.println(exec);
          }catch (Exception e){
              e.printStackTrace();
              multi.discard();
          }finally {
              jedis.unwatch();
              jedis.close();
          }
      }
      public static void main(String[] args) {
          Jedis jedis=new Jedis("192.168.126.130",6379);
          jedis.auth("123456");
          jedis.set("ticket","1");
          jedis.set("money","0");

          Thread t1=new Thread(()->{
              secKill();
          });
          Thread t2=new Thread(()->{
              secKill();
          });
          t1.start();
          t2.start();
      }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习Redis的测试人员可以按照以下步骤进行学习和实践: 1. 首先,测试人员可以从Redis的官网(https://redis.io/download)上下载最新稳定版本的Redis并进行安装。 2. 在安装完成后,测试人员可以启动Redis服务,可以使用命令redis-server /usr/local/etc/redis.conf来启动Redis服务。 3. 了解Redis的五大数据类型: 字符串(String)、哈希(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set)。可以通过阅读Redis的官方文档或参考其他教程来学习这些数据类型的使用方法。 4. 学习Redis的持久化机制,了解Redis如何将数据保存到磁盘上以确保数据的持久性。 5. 了解Redis事务处理机制,学习如何使用MULTI、EXEC、WATCH等指令来实现事务操作。 6. 学习Redis的主从复制机制,了解如何配置主从服务器以实现数据的复制和高可用性。 7. 在学习过程中,测试人员可以通过实践来加深对Redis的理解和掌握。可以使用Redis客户端工具或编写代码与Redis进行交互,尝试使用Redis提供的各种功能。 8. 如果测试人员在学习过程中遇到问题,可以在Redis的官方论坛或其他技术社区中提问,寻求帮助和解答。 通过以上步骤,测试人员可以系统地学习和掌握Redis的基础知识,并深入理解Redis的各种功能和用法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [测试人员应该知道的Redis知识(一) 概述](https://blog.csdn.net/Frank_girl/article/details/107720322)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值