IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> Redis事务和锁机制(乐观锁+秒杀) -> 正文阅读

[大数据]Redis事务和锁机制(乐观锁+秒杀)

目录

命令

组队Multi错误(命令此时不会真正执行):

执行exec错误:

事务冲突

?解决方案

悲观锁:

乐观锁:

场景:

?演示乐观锁,watch key监控

Redis事务总结:

秒杀案例

ab测压使用教程

?连接超时问题:

超卖问题

Redis使用乐观锁库存遗留问题:


Redis事务是一个单独的隔离操作,事务中的所有命令都会序列化,按顺序的执行,就像上述图一样,不会被别的客户端送来的命令请求所打断;

目的:串联多个命令,防止别的命令插队

命令

Multi(组队阶段):将命令按顺序放到队列中,但是不会执行

Exec(执行阶段):提交,类似于commit,提交完后就代表事务已经结束了

discard(回滚阶段):如果你觉得执行的命令有问题,可以使用该阶段来放弃组队;

?

discard:代表上述组队已经被放弃了?,前面的命令作废

组队Multi错误(命令此时不会真正执行):

在组队时期(Multi)出现错误时,如果你提交事务exec,那么之前所有的命令都会作废;?

执行exec错误:

?我认为,Multi与exec发生的错误有点像编译错误和运行时错误,如果你语法啥的都错了,那程序直接挂了过不了

运行错误的话,你好歹还有class文件,所以说你能拿的东西还是能拿到,错的就死;


事务冲突

?例子:

?解决方案

悲观锁:

?操作之前先上锁(别人不能进行操作,阻塞了),等执行的人操作完了之后,会将锁释放,然后另外的人就会上锁,并且执行操作。。。:效率低下

乐观锁:

?

顾名思义,就是很乐观,每次去拿数据的时候都会认为别人没有修改数据,所以不会上锁,但是自己在更新数据的时候会判断在此期间,别人到底有没有更新数据,(这里我们用版本号来表示),更新数据了的版本号和没更新数据的不一致;——>所以说,如果别人更新了,那么自己就更新不了了;但是别人还是可以继续更新的;

适用于多读类型,有利于提高吞吐量(因为时间快了,自然吞吐量就高了);

场景:

多个人抢一张票,可能都抢到票了,但是支付成功的就一个;

抢到票可以理解为入队操作Multi;

支付成功可以理解为事务提交exec;

?演示乐观锁,watch key监控

?理解:一般是和事务一起用,当某个key进行watch之后,如果其他客户端对这个key进行了更改,那么本次事务将会被取消,事务的exec会返回null,jedis.watch(key)会返回ok

比如:

已经有第一个人对这个key进行更改了,所以其他人再对这个key执行时(exec)会返回?null

?


?

第一个用户对balance执行命令?

1、watch balance:监控键balance

2、multi:开始排队

3、incrby 键 10:对键对应的value+10操作

4、exec:执行操作

此时反馈为110

?

第二个用户同样的操作会提示失败,为null,因为?第二个的版本号与第一个更新后的版本号不一致

?


Redis事务总结:

Redis是没有原子性的,在exec执行阶段如果出现错误,只是出现错误的命令不能执行;

秒杀案例

利用ab命令进行测压,?设置并发

?

?并发花费时间

两个键:用户秒杀成功key,储存商品key?

?

设置仓库商品数量 set 键名 数量?

?

并发完后,查询仓库商品信息,变成负数了?

ab测压使用教程

(9条消息) ab压测工具使用教程_u011585609的专栏-CSDN博客_ab工具使用方法?

?连接超时问题:

?当redis处理不了更多的请求,那么那些请求就会一直等待;

解决:用类似mysql中的连接池解决即可

?在JedisUtils配置Jedis连接池配置信息

package com.atguigu;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisPoolUtil {
	//初始化Jedis连接池
	private static volatile JedisPool jedisPool = null;

	private JedisPoolUtil() {
	}

	//配置连接池的配置信息
	public static JedisPool getJedisPoolInstance() {
		if (null == jedisPool) {
			synchronized (JedisPoolUtil.class) {
				if (null == jedisPool) {
					JedisPoolConfig poolConfig = new JedisPoolConfig();
					//配置最大连接次数
					poolConfig.setMaxTotal(200);
					poolConfig.setMaxIdle(32);
					poolConfig.setMaxWaitMillis(100*1000);
					poolConfig.setBlockWhenExhausted(true);
					poolConfig.setTestOnBorrow(true);  // ping  PONG
				 
					jedisPool = new JedisPool(poolConfig, "192.168.44.168", 6379, 60000,"123456");
				}
			}
		}
		return jedisPool;
	}

	public static void release(JedisPool jedisPool, Jedis jedis) {
		if (null != jedis) {
			jedisPool.returnResource(jedis);
		}
	}

}

超卖问题

明明东西卖完了, 但是还提示秒杀成功

?当库存为1时,同时有n个连接一起执行减少库存操作时,-n+1;

超卖问题,需要用乐观锁方式解决:

watch监控key,然后将命令加入排队,执行;

package com.atguigu;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.LoggerFactory;

import ch.qos.logback.core.rolling.helper.IntegerTokenConverter;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.Transaction;

/**
 *
 */
public class SecKill_redis {

	public static void main(String[] args) {
		Jedis jedis =new Jedis("192.168.44.168",6379);
		System.out.println(jedis.ping());
		jedis.close();
	}

	//秒杀过程
	public static boolean doSecKill(String uid,String prodid) throws IOException {
//      1、uid和prodid非空判断
		if(uid==null|| prodid==null){
			return false;
		}

//		2、连接redis
//		Jedis jedis = new Jedis("192.168.184.131", 6379);
//        jedis.auth("123456");

//		2.2通过连接池获取jedis对象
		JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance();
		Jedis jedis = jedisPoolInstance.getResource();

//      3、 拼接key,库存key
        String kcKey="sk:"+prodid+":qt";

//		3.1秒杀成功的用户id
		String userKey="sk:"+prodid+":user";

//		监视watch一下库存
		 jedis.watch(kcKey);

//		4.获取库存,如果库存==null,秒杀还没开始
		String kc = jedis.get(kcKey);
		if(kc==null){
			System.out.println("秒杀还没开始,请等待");
			jedis.close();
			return false;
		}

//		5.判断用户是否重复秒杀(用set),看用户秒杀清单(userKey)是否有该用户(uid)
		Boolean sismember = jedis.sismember(userKey, uid);
        if(sismember){
		   System.out.println("已经秒杀成功了,不能重复秒杀");
		   jedis.close();
		   return false;
	   }

//		6.判断商品数量,如果<1,秒杀结束
		if(Integer.parseInt(kc)<0){
			System.out.println("秒杀已经结束了");
			jedis.close();
			return false;
		}

//		7.秒杀过程
//		使用事务
		Transaction multi = jedis.multi();

//		组队操作:将库存中的商品数量-1,秒杀的用户uid添加到userkey中
		multi.decr(kcKey);
		multi.sadd(userKey,uid);

//		执行
		List<Object> results = multi.exec();

		if(results==null||results.size()==0){
			System.out.println("秒杀失败.....");
			jedis.close();
		}

//		7.1储存-1
//		jedis.decr(kcKey);

//		7.2把秒杀成功的用户添加到秒杀清单中
//		jedis.sadd(userKey,uid);
//		System.out.println("秒杀成功..");
//		jedis.close();

	return true;
	}
}


Redis使用乐观锁库存遗留问题:

比如说:当一个用户购买成功后,而商品此时版本号变更,那么其他用户就不能往下面进行了;

利用lua脚本:

package com.atguigu;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.LoggerFactory;

import ch.qos.logback.core.joran.conditional.ElseAction;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.Transaction;

public class SecKill_redisByScript {
	
	private static final  org.slf4j.Logger logger =LoggerFactory.getLogger(SecKill_redisByScript.class) ;

	public static void main(String[] args) {
		JedisPool jedispool =  JedisPoolUtil.getJedisPoolInstance();
 
		Jedis jedis=jedispool.getResource();
		System.out.println(jedis.ping());
		
		Set<HostAndPort> set=new HashSet<HostAndPort>();

	//	doSecKill("201","sk:0101");
	}
	
	static String secKillScript ="local userid=KEYS[1];\r\n" + 
			"local prodid=KEYS[2];\r\n" + 
			"local qtkey='sk:'..prodid..\":qt\";\r\n" + 
			"local usersKey='sk:'..prodid..\":usr\";\r\n" + 
			"local userExists=redis.call(\"sismember\",usersKey,userid);\r\n" + 
			"if tonumber(userExists)==1 then \r\n" + 
			"   return 2;\r\n" + 
			"end\r\n" + 
			"local num= redis.call(\"get\" ,qtkey);\r\n" + 
			"if tonumber(num)<=0 then \r\n" + 
			"   return 0;\r\n" + 
			"else \r\n" + 
			"   redis.call(\"decr\",qtkey);\r\n" + 
			"   redis.call(\"sadd\",usersKey,userid);\r\n" + 
			"end\r\n" + 
			"return 1" ;
			 
	static String secKillScript2 = 
			"local userExists=redis.call(\"sismember\",\"{sk}:0101:usr\",userid);\r\n" +
			" return 1";

	public static boolean doSecKill(String uid,String prodid) throws IOException {

		JedisPool jedispool =  JedisPoolUtil.getJedisPoolInstance();
		Jedis jedis=jedispool.getResource();

		 //String sha1=  .secKillScript;
		String sha1=  jedis.scriptLoad(secKillScript);
		Object result= jedis.evalsha(sha1, 2, uid,prodid);

		  String reString=String.valueOf(result);
		if ("0".equals( reString )  ) {
			System.err.println("已抢空!!");
		}else if("1".equals( reString )  )  {
			System.out.println("抢购成功!!!!");
		}else if("2".equals( reString )  )  {
			System.err.println("该用户已抢过!!");
		}else{
			System.err.println("抢购异常!!");
		}
		jedis.close();
		return true;
	}
}

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-03-11 22:17:06  更:2022-03-11 22:17:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:03:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码