多线程操作 redis 会有并发问题嘛?

2019-08-01 15:42:20 +08:00
 Buffer2Disk

如题, 我看网上都说 redis 是单线程的,内部使用 epoll

我这边使用 jedis 来操作 redis, jedis 从 jedisPool 中取出来

有两个线程 1,2

线程 1 不断的 get ( keyA )

线程 2 不断的 lpop (keyB)

keyA 和 keyB 是 2 个不同的键

现在我发现在并发操作的时候,lpop 有一定几率 会把 keyA 里面数据给拿出来,get 操作 有一定几率会拿到 keyB 的数据

这是什么鬼。。。。。

6639 次点击
所在节点    程序员
31 条回复
Buffer2Disk
2019-08-01 15:49:57 +08:00
使用的可以是这种格式的

keyA queue:test

keyB data:overload:user
Buffer2Disk
2019-08-01 15:50:32 +08:00
抽风了, 使用的 key 是上面这种格式的
ke1e
2019-08-01 15:59:26 +08:00
这是 jedis 的问题吧
arrow8899
2019-08-01 16:04:53 +08:00
发一下代码吧,估计是代码写的有问题。
rrfeng
2019-08-01 16:05:51 +08:00
用了同一个 connection 吧,估计是用错了。
msg7086
2019-08-01 16:11:32 +08:00
看上去复用了一个网络连接,所以线程 1 发出请求的回答被线程 2 收走了。
pelloz
2019-08-01 16:12:53 +08:00
请用 Lettuce
Buffer2Disk
2019-08-01 16:13:02 +08:00
@rrfeng
@arrow8899
@ke1e

private static void initialPool() {
ApplicationContext applicationContext =
new FileSystemXmlApplicationContext("classpath:config/spring-application-redis.xml");
jedisPool = (JedisPool) applicationContext.getBean("jedisPool");
logger.info("<---jedis pool init--->");
}

/**
* 初始化,加锁防止被多次初始化 pool
*/
private synchronized static void poolInit() {
if (jedisPool == null) {
initialPool();
}
}

/**
* 获取 Jedis 实例
*
* @return Jedis
*/
public static Jedis getJedis() {
poolInit();
Jedis jedis = null;
try {
if (jedisPool != null) {
jedis = jedisPool.getResource();
}
} catch (Exception e) {
logger.error("get jedis error",e);
} finally {
returnResource(jedis);
logger.debug("<---jedis return resource--->");
}
return jedis;
}


/**
* 释放 jedis 资源
*
* @param jedis
*/
public static void returnResource(final Jedis jedis) {
if (jedis != null) {
jedis.close();
}
}


public static String get(String key) {
try {
return getJedis().get(key);
} catch (Exception ex) {
logger.error("redis get error",ex);
}
return null;
}


public static String lpop(String key) {
try {
return getJedis().lpop(key);
} catch (Exception ex) {
logger.error("lpop error , key = {}", key, ex);
}
return null;
}
cheng6563
2019-08-01 16:14:29 +08:00
jedis 是线程不安全的
请使用 redisson
Buffer2Disk
2019-08-01 16:14:51 +08:00
@msg7086 还有复用同一个网络连接这么一说
Buffer2Disk
2019-08-01 16:19:09 +08:00
@cheng6563
我看网上说直接使用 jedis 是线程不安全的,但是使用 JedisPool 是线程安全的,
我使用的就是 jedisPool 来获取 jedis 实例



Jedis 不是线程安全的,故不应该在多线程环境中共用一个 Jedis 实例。但是,也应该避免直接创建多个 Jedis 实例,因为这种做法会导致创建过多的 socket 连接,性能不高。
要保证线程安全且获得较好的性能,可以使用 JedisPool。JedisPool 是一个连接池,既可以保证线程安全,又可以保证了较高的效率。
可以声明一个全局的 JedisPool 变量来保存 JedisPool 对象的引用,然后在其他地方使用。要知道,JedisPool 是一个线程安全的连接池。
jorneyr
2019-08-01 16:31:51 +08:00
} finally {
returnResource(jedis);
logger.debug("<---jedis return resource--->");
}
return jedis;

这是什么操作,返回后马上把 redis 关闭
Jrue0011
2019-08-01 16:42:14 +08:00
看问题是拿到了同一个 jedis
原因应该是出在你获取 jedis 的代码,获取了 jedis 你还没用,你就在 finally 处调用了 jedis.close 将连接返回了连接池,导致连接池存在可用的 jedis,不会创建新对象,下一次拿到的还是相同的 jedis
LeeSeoung
2019-08-01 16:42:29 +08:00
关注,有大佬找到原因的话告知下。。
glues
2019-08-01 16:51:12 +08:00
这一看就是 jedis 的问题,或者是你用的不对
Buffer2Disk
2019-08-01 16:54:35 +08:00
知道
@jorneyr 尴尬,应该是
public static Jedis getJedis() {
poolInit();
Jedis jedis = null;
try {
if (jedisPool != null) {
jedis = jedisPool.getResource();
}
} catch (Exception e) {
logger.error("get jedis error", e);
returnResource(jedis);
logger.debug("<---jedis return resource--->");
}
return jedis;
}
Aresxue
2019-08-01 16:55:53 +08:00
歪个楼,初始化 JedisPool 用单例的安全检查或者静态内部类,别加锁。。。Jedis 不是熟,但是 Lettuce 现在 shi 更流行也更好用,所以换 Lettuce 是最好的方式。
Buffer2Disk
2019-08-01 16:55:54 +08:00
@Jrue0011 大佬,为什么拿到相同的 jedis,使用的 key 是不同的,为什么会拿到别的 key 的结果呢?
Jrue0011
2019-08-01 17:07:29 +08:00
@Buffer2Disk 额。。你追踪下 Jedis 源码就知道了。一个 jedis -> client -> connection 维护一个 outputStream 和一个 inputStream,命令用 outputStream 发送,响应从 inputStream 读取,发送命令和读取响应的方法没加锁,一个线程读到另一个线程本该接收的响应很正常。。
Buffer2Disk
2019-08-01 17:56:16 +08:00
@Jrue0011 改了以后确实可以了,有一个问题就是,jedis close() 后难道不是立马就释放回连接池的嘛?

我试了下循环一百次 get 操作,很容易就触发到 maxTotal 的阈值了,然后程序就一直 hang 在那里,像是在等待连接池释放新的连接出来一样,但是等了很久也没有出来,这个是有什么参数可以控制的么

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/588230

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX