Redis:解决分布式高并发修改同一个Key的问题

更新时间:2023-06-14 02:52:19 阅读: 评论:0

Redis:解决分布式⾼并发修改同⼀个Key的问题
本篇⽂章是通过watch(监控)+mutil(事务)实现应⽤于在分布式⾼并发处理等相关场景。下边先通过来测试多个线程修改时,遇到问题及解决问题。
⾼并发下修改同⼀个key遇到的问题:
1)定义⼀个hash类型的key,key为:lock_test,元素locker的值初始化为0。
2)实现⾼并发下对locker元素的值递增:定义64个多线程,并发的对lock_test元素locker的值进⾏修改。
package com.dx.es;
import urrent.CountDownLatch;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
北京温泉酒店
public class Test_UnLock {木瓜排骨
public static void main(String[] args) {
final JedisPool pool = Pool();
// 获得jedis对象
Jedis jedis = Resource();
jedis.ht("lock_test", "locker", "0");
String val = jedis.hget("lock_test", "locker");
System.out.println("lock_test.locker的初始值為:" + val);
jedis.clo();
int threahSize = 64;
final CountDownLatch threadsCountDownLatch = new CountDownLatch(threahSize);
Runnable handler = new Runnable() {
public void run() {
Jedis jedis = Resource();
Integer integer = Integer.valueOf(jedis.hget("lock_test", "locker"));
jedis.ht("lock_test", "locker", String.valueOf(integer + 1));
jedis.clo();
}
};
for (int i = 0; i < threahSize; i++) {
如何进行网络推广new Thread(handler).start();
}
/
/ 等待所有并⾏⼦线程任务完成。
try {三余指的是什么
threadsCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("complete");
val = jedis.hget("lock_test", "locker");
System.out.println(val);
}
}
RedisUtil.java
package com.dx.es;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisUtil {
public static JedisPool getPool() {
// 简单创建 Jedis的⽅法:
// final Jedis jedis = new Jedis("127.0.0.1",6379);
// 下边使⽤线程池的⽅案:jedis对象是线程不安全的,因此在并发情况下要使⽤JedisPool,默认情况下jedisPool只⽀持8个连接,因此在声明JedisPool时要先修改JedisPool的最⼤连接数        JedisPoolConfig config = new JedisPoolConfig();
// 修改最⼤连接数
config.tMaxTotal(32);
// 声明⼀个线程池
JedisPool pool = new JedisPool(config, "127.0.0.1", 6379);
return pool;
}
}
此时,会出现以下问题:
1. A线程获取key的值为0,⽽B线程也获取jkey的值0,则A把key值递增为1,B线程也实现把key值递增为1。两个线程都执⾏了key值修改:0到1。
2. 在1)中最终key修改为了1,但是c线程获取key的值为0(因为c线程读取key值时,a、b线程还未触发修改,因此c线程读取到的值为0),此时d线程读取到的值为1(因为d线程读取
key值时,a、b线程已触发修改,⼀次d线程取到的值为1)。
3. 此时假设d线程优先触发递增,则在c线程未触发提交之前d线程已经把值修改了2,但是c此时并不知道在它获取到值到修改之前这段时间发⽣了什么,直接把值修改1。
此时执⾏打印结果为:
lock_test.locker的初始值為:0
complete
24 #备注:也可能是其他值,可能是正确值64的可能性⽐较⼩。
通过watch+mutil 解决并发修改的问题:
需要掌握Redis 事务命令:
下表列出了 redis 事务的相关命令:
序号命令描述
可⽤版
1 DISCARD Redis Discard 命令⽤于取消事务,放弃执⾏事务块内的所有命令。
语法 redis Discard 命令基本语法如下:
redis 127.0.0.1:6379> DISCARD
>= 2.0.0
2 EXEC Redis Exec 命令⽤于执⾏所有事务块内的命令。
语法 redis Exec 命令基本语法如下:
redis 127.0.0.1:6379>Exec
>= 1.2.0
3 MULTI Redis Multi 命令⽤于标记⼀个事务块的开始。
事务块内的多条命令会按照先后顺序被放进⼀个队列当中,最后由 EXEC 命令原⼦性(atomic)地执⾏。
语法 redis Multi 命令基本语法如下:
redis 127.0.0.1:6379>Multi
>= 1.2.0
4 UNWATCH Redis Unwatch 命令⽤于取消 WATCH 命令对所有 key 的监视。
语法 redis Unwatch 命令基本语法如下:
redis 127.0.0.1:6379> UNWATCH
>= 2.2.0
5 WATCH Redis Watch 命令⽤于监视⼀个(或多个) key ,如果在事务执⾏之前这个(或这些) key 被其他命令所改动,那么事务将
被打断
语法 redis Watch 命令基本语法如下:
WATCH key [key ...]
>= 2.2.0
Redis 事务可以⼀次执⾏多个命令,并且带有以下两个重要的保证:
批量操作在发送 EXEC 命令前被放⼊队列缓存。
收到 EXEC 命令后进⼊事务执⾏,事务中任意命令执⾏失败,其余的命令依然被执⾏。
在事务执⾏过程,其他客户端提交的命令请求不会插⼊到事务执⾏命令序列中。
⼀个事务从开始到执⾏会经历以下三个阶段:
开始事务。
命令⼊队。
执⾏事务。
备注:概念性摘⾃《/redis/redis-transactions.html 》
<下的事务操作:
# 事务被成功执⾏
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> INCR ur_id
QUEUED
redis 127.0.0.1:6379> INCR ur_id
长沙有什么好吃的QUEUED
redis 127.0.0.1:6379> INCR ur_id
QUEUED
redis 127.0.0.1:6379> PING
QUEUED
redis 127.0.0.1:6379> EXEC
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) PONG
熟练
并发情况下使⽤watch+mutil操作:
事务块内所有命令的返回值,按命令执⾏的先后顺序排列。当操作被打断时,返回空值 nil 。A线程:
# 监视 key ,且事务成功执⾏
redis 127.0.0.1:6379> WATCH lock lock_times
OK
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> SET lock "huangz"
QUEUED
redis 127.0.0.1:6379> INCR lock_times
QUEUED
redis 127.0.0.1:6379> EXEC
1) OK
2) (integer) 1
B线程:
# 监视 key ,且事务被打断
redis 127.0.0.1:6379> WATCH lock lock_times
OK
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> SET lock "joe"        # 就在这时,另⼀个客户端修改了 lock_times 的值QUEUED
redis 127.0.0.1:6379> INCR lock_times
QUEUED
redis 127.0.0.1:6379> EXEC                  # 因为 lock_times 被修改, joe 的事务执⾏失败(nil)
上边演⽰了A、B线程并发下的watch+mutil操作情况。
解决⾼并发下修改同⼀个key遇到的问题:
package com.dx.es;
import java.util.List;
import urrent.CountDownLatch;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;
public class Test_Lock3 {
public static void main(String[] args) {
final JedisPool pool = Pool();
出行计划
// 对测试key赋初始值
Jedis jedis = Resource();
jedis.ht("lock_test", "locker", "0");
String val = jedis.hget("lock_test", "locker");
System.out.println("lock_test.locker的初始值為:" + val);
jedis.clo();
int threahSize = 64;
final CountDownLatch threadsCountDownLatch = new CountDownLatch(threahSize);
Runnable handler = new Runnable() {
public void run() {
Jedis jedis = Resource();
while (true) {
jedis.watch("lock_test");
String val = jedis.hget("lock_test", "locker");
Integer integer = Integer.valueOf(val);
Transaction tx = jedis.multi();
tx.ht("lock_test", "locker", String.valueOf(integer + 1));
List<Object> exec = tx.exec();
if (exec == null || exec.isEmpty()) {
System.out.println(Thread.currentThread().getName() + ":" + "Error:(" + val + "=>" + (integer + 1) + ")");
} el {
String values = "";
for (int i = 0; i < exec.size(); i++) {
values += (i).toString();
}
System.out.println(Thread.currentThread().getName() + ":" + values + ":(" + val + "=>" + (integer + 1) + ")");
break;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
jedis.clo();
}
};
for (int i = 0; i < threahSize; i++) {
new Thread(handler).start();
}
// 等待所有并⾏⼦线程任务完成。
try {
threadsCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("complete");
val = jedis.hget("lock_test", "locker");
System.out.println(val);
}
}
打印结果:
lock_test.locker的初始值為:0
Thread-8:0:(0=>1)
Thread-56:Error:(1=>2)
Thread-53:0:(1=>2)
Thread-25:Error:(0=>1)
Thread-18:Error:(0=>1)
Thread-3:Error:(0=>1)
Thread-30:Error:(2=>3)
Thread-11:Error:(2=>3)
Thread-9:Error:(0=>1)
Thread-63:0:(2=>3)
Thread-7:Error:(0=>1)
Thread-10:0:(3=>4)
Thread-34:0:(4=>5)
Thread-65:Error:(4=>5)
Thread-24:Error:(0=>1)
Thread-17:Error:(0=>1)
Thread-62:0:(5=>6)
Thread-29:Error:(6=>7)
Thread-61:0:(6=>7)
Thread-64:0:(7=>8)
Thread-16:Error:(0=>1)
Thread-19:Error:(8=>9)
Thread-6:Error:(0=>1)
Thread-28:Error:(0=>1)
Thread-21:Error:(0=>1)
Thread-14:Error:(0=>1)
Thread-20:Error:(0=>1)
Thread-5:Error:(0=>1)
Thread-13:Error:(0=>1)
Thread-15:Error:(0=>1)
Thread-22:Error:(0=>1)
Thread-4:Error:(0=>1)
Thread-12:Error:(0=>1)
Thread-23:Error:(0=>1)
Thread-54:Error:(0=>1)
Thread-57:Error:(0=>1)
Thread-26:0:(8=>9) Thread-27:Error:(8=>9) Thread-32:Error:(9=>10) Thread-35:Error:(9=>10) Thread-56:0:(9=>10) Thread-33:Error:(10=>11) Thread-50:0:(10=>11) Thread-31:0:(11=>12) Thread-38:0:(12=>13) Thread-25:Error:(13=>14) Thread-36:0:(13=>14) Thread-39:Error:(14=>15) T
hread-14:0:(14=>15) Thread-19:Error:(14=>15) Thread-17:Error:(14=>15) Thread-6:Error:(14=>15) Thread-9:Error:(14=>15) Thread-33:Error:(14=>15) Thread-35:Error:(14=>15) Thread-23:Error:(14=>15) Thread-18:Error:(14=>15) Thread-15:Error:(14=>15) Thread-11:Error:(14=>15) Thread-7:Error:(14=>15) Thread-57:Error:(14=>15) Thread-27:Error:(14=>15) Thread-16:Error:(14=>15) Thread-65:Error:(14=>15) Thread-24:Error:(14=>15) Thread-13:Error:(14=>15) Thread-32:Error:(15=>16) Thread-28:Error:(14=>15) Thread-21:Error:(14=>15) Thread-30:Error:(14=>15) Thread-54:Error:(14=>15) Thread-22:Error:(14=>15) Thread-25:Error:(14=>15) Thread-3:Error:(14=>15) Thread-29:Error:(14=>15) Thread-5:Error:(14=>15) Thread-12:Error:(14=>15) Thread-20:Error:(14=>15) Thread-40:0:(15=>16) Thread-4:Error:(14=>15) Thread-41:0:(16=>17) Thread-44:0:(17=>18) Thread-45:0:(18=>19) Thread-47:0:(19=>20) Thread-43:0:(20=>21) Thread-48:0:(21=>22) Thread-37:0:(22=>23) Thread-49:0:(23=>24) Thread-55:0:(24=>25) Thread-60:0:(25=>26) Thread-42:0:(26=>27) Thread-52:0:(27=>28) Thread-46:0:(28=>29) Thread-58:0:(29=>30) Thread-51:0:(30=>31) Thread-66:0:(31=>32) Thread-59:0:(32=>33) Thread-17:0:(33=>34) Thread-19:Error:(33=>34) Thread-39:Error:(33=>34) Thread-28:0:(34=>35) Thread-54:Error:(34=>35) Thread-65:Error:(34=>35) Thread-25:Error:(34=>35) Thread-30:Error:(34=>35) Thread-5:Error:(35=>36) Thread-13:Error:(35=>36) Thread-16:Error:(34=>35) Thread-6:Error:(34=>35) Thread-9:Error:(34=>35) Thread-21:Error:(35=>36) Thread-29:Error:(35=>36) Thread-33:Error:(34=>35) Thread-57:
Error:(35=>36) Thread-24:Error:(34=>35) Thread-22:Error:(34=>35) Thread-32:Error:(35=>36) Thread-23:Error:(34=>35)
>唯有相思

本文发布于:2023-06-14 02:52:19,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/949356.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:命令   线程   事务
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图