Redis是单线程加多路IO复原的形式,MemCache是多线程加锁的显示
Redis本身是单线程的形式,所以Redis的操作是本身就具有原子性的,每执行的命令都是有原子性的
原子性:指不会被线程调度机制打断的操作
操作一旦执行,就一直运行到结束,中间不会有任何线程将操作切换进来
- 在单线程中,能够再单条指令中完成的操作都可以被认为是"原子性操作",因为中断只能发生再指令之间
- 在多线程中,不能被其他进程(线程)打断的操作也叫原则性操作
Redis的原则性主要由它本身的单线程所决定的
Redis也提供了事务的操作
一,Redis事务的概念
Redis事务是一个单独的隔离操作:事务中的所有命令都会被序列化,按顺序执行执行,事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
Redis事务的主要作用:串联多个命令防止别的命令插队
Redis事务的三大命令:
- Multi(组队阶段):开启一个事务,所有操作依次进入队列(不执行)
- Exec(执行阶段):执行事务中的命令,依次执行
- Discard:组队阶段中放弃组队
- 组队阶段命令出错误,执行exec时这个事务的所有操作都会被取消
- 执行阶段命令出错误,那个错误命令会报错不会执行,其他命令不受影响,这个时候它不像MySql是原子性的
一,Redis事务的冲突问题(悲观锁和乐观锁)
事务冲突主要发生在并发情况下,当多个用户同时修改一条数据时就会出现事务冲突问
例:现在有redis客户端,设置money为1000,有三个连接客户端分别请求服务器并修改money数据
解决这种事务冲突的方案主要有两种:
- 悲观锁
- 乐观锁
二,悲观锁
悲观锁,顾名思义就是对未来的操作很悲观,每一次拿数据的时候都认为别人会修改,所以在每一次在拿数据的时候都会上锁,这样别人想拿这个数据就需要先block锁,直到拿到锁,一次只有一个线程能拿到锁
悲观锁的实现本质上就是加锁,传统的关系型数据库就是使用悲观锁,比如MySql的行锁,表锁,读锁,写锁等都是悲观锁的实现
悲观锁本质上操作是串行的,没有获取到锁的对象需要进行等待,依次获取锁执行,效率很低
注:Redis并没有自带悲观锁的操作,要实现悲观锁需要使用外部编程的方式的实现
三,乐观锁
乐观锁,顾名思义就是很乐观,每一次拿数据的时候都认为其他用户不会做修改,所以不会上锁,但是在更新的时候会判断一下在此期间有没有用户去更新这个数据,它的实现主要提供版本号,可以版本号判断其他用户有没有做修改
乐观锁:用于多读的应用类型,这样可以提高吞吐量,Redis本身自带了乐观锁机制(Watch)
四,Redis乐观锁操作
Redis默认自带了乐观锁,通过命令 WATCH key [key……]
在执行Multi之前,先执行watch key1 [key2],可以监视一个或多个key,如果事务在exec执行之前监听的key被其他命令改动,那么事务就会被打断执行失败
命令:
- watch key:监听key(本质上就是在key上面加一个版本号)
- unwatch:取消监听
通过乐观锁和基本使用分析Redis的事务特性:
- 单独的隔离操作:事务中的所有命令都会被序列化,按照顺序执行,事务在执行过程中不会被其他客户端发来的命令请求打断
- 没有隔离级别的概念:队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
- 不保证原子性:事务中如果有一条命令执行失败,其后的命令依然会被执行,没有回滚
二,通过Redis实现简单秒杀案例
一,无事务秒杀
一,无事务秒杀代码
① controll层
@Controller public class sckillcontrol { //秒杀的service @Autowired Seckillservice seckillservice; //虚拟用户id,没有前端不能从前端获取 Random random = new Random(); @RequestMapping("/seckill") @ResponseBody public String seckill(){ //没有前端,固定商品id String commodity_id="1001"; //每一次秒杀随机用户id int userid = random.nextInt(1000); //传入service boolean b = seckillservice.seckill_service(commodity_id, userid); if(!b){ return "秒杀失败!"; } return "秒杀成功!"; }
② service层
@Service public class Seckillservice { @Autowired RedisTemplate redisTemplate; public boolean seckill_service(String commodityid,int userid){ //1,判断商品id和用户id是否为空 if(commodityid.equals("") || userid==0 ){ return false; } //2,对commodityid和userid做为key进行拼接 String commodity = "commodit:"+commodityid; String user = "user:size"; //3,判断redis中commodityid是否存在,不存在则秒杀没有开始 Object o = redisTemplate.opsForValue().get(commodity); if(o==null){ System.out.println("秒杀未开始!"); return false; } //4,判断commodity是否还要库存 Integer quantity = (Integer)redisTemplate.opsForValue().get(commodity); if(quantity>1){ System.out.println("库存不够!"); return false; } //5,判断用户是否重复操作 if(redisTemplate.opsForSet().isMember(user,userid)){ System.out.println("用户重复操作!"); return false; } //6,秒杀库存 //库存减一 redisTemplate.opsForValue().decrement(commodity, 1); //秒杀成功的用户添加到用户set集合中 redisTemplate.opsForSet().add(user,userid); return true; } }
Comments | NOTHING
Warning: Undefined variable $return_smiles in /www/wwwroot/wql_luoqin_ltd/wp-content/themes/Sakura/functions.php on line 1109