spring-data-redis中的坑和误区
来源:互联网 发布:c语言输出整数 编辑:程序博客网 时间:2024/06/06 02:32
spring的redis模板中有问题的地方
在项目开发过程中,想要进行redis的并发控制,这时候,想当然地使用了spring-data-redis库中template里面提供的multi()和exec()方法,如下:
redis.multi() // do something hereredis.exec()
使用了之后,就出现了如下异常:
org.springframework.dao.InvalidDataAccessApiUsageException: ERR EXEC without MULTI; nested exception is redis.clients.jedis.exceptions.JedisDataException: ERR EXEC without MULTI
在google搜了一下,才知道,原来是目前该库的redistemplate的multi和exec方法,都是新产生连接,而非使用本来的连接,这个异常,也是由于这个原因所以才导致的。(因为新连接中,直接执行退出同步,系统肯定会去找是从哪儿开始同步的,这一找,发现没有开始同步的命令,就会抛出异常了)
这个时候,只能自己调用底层的RedisCallBack和Jedis去实现底层redis语句了。类似这样:
Jedis jedis = new Jedis("localhost",6379); new RedisCallback<Object>() { public Object doInRedis(RedisConnection connection) throws DataAccessException { connection.multi(); return null; } }.doInRedis(new JedisConnection(jedis));
这时候肯定会有个疑问,既然这个template每次都会生成新连接,那这个multi和exec命令还有什么用?官方是这么回答的:
The methods are exposed in case the connection is shared across methods. Currently we don’t provide any out of the box support for connection binding but the RedisTemplate supports it - just like with the rest of the templates, one connection could be bound to the running thread and the RT will use it without creating new ones.
大致意思是这些方法目前是没有用的。但实际上这个模板是能够支持的。可以看到RedisTemplate中有这么一个方法:
<T> T execute(SessionCallback<T> session);
其参数类型是SessionCallBack而不是RedisCallback。我们平时在persist、或者Ofs的时候,模板默认调用的是参数类型为RedisCallback的函数:
<T> T execute(RedisCallback<T> action);
我们看一下他的实现:
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) { Assert.notNull(action, "Callback object must not be null"); RedisConnectionFactory factory = getConnectionFactory(); RedisConnection conn = RedisConnectionUtils.getConnection(factory); boolean existingConnection = TransactionSynchronizationManager.hasResource(factory); preProcessConnection(conn, existingConnection); boolean pipelineStatus = conn.isPipelined(); if (pipeline && !pipelineStatus) { conn.openPipeline(); } try { RedisConnection connToExpose = (exposeConnection ? conn : createRedisConnectionProxy(conn)); T result = action.doInRedis(connToExpose); // close pipeline if (pipeline && !pipelineStatus) { conn.closePipeline(); } // TODO: any other connection processing? return postProcessResult(result, conn, existingConnection); } finally { RedisConnectionUtils.releaseConnection(conn, factory); }}
虽然是从工厂拿出来的,但是可以预见其与并无法将事务与该连接绑定。而我们再来看下参数类型为SessionCallback的execute方法源码:
public <T> T execute(SessionCallback<T> session) { RedisConnectionFactory factory = getConnectionFactory(); // bind connection RedisConnectionUtils.bindConnection(factory); try { return session.execute(this); } finally { RedisConnectionUtils.unbindConnection(factory); }}
可以看出来,其会对连接进行绑定和解绑。因而其从功能上应该是能够实现对事务的支持的。在CollectionUtils源码中,可以找到对该方法的运用,可以很明显看到。其通过如下代码实现了监听+事务管理。
static <K> void rename(final K key, final K newKey, RedisOperations<K, ?> operations) { operations.execute(new SessionCallback<Object>() { @SuppressWarnings("unchecked") public Object execute(RedisOperations operations) throws DataAccessException { do { operations.watch(key); if (operations.hasKey(key)) { operations.multi(); operations.rename(key, newKey); } else { operations.multi(); } } while (operations.exec() == null); return null; } });}
除此之外,iteye上还有人指出了这个模板的其他问题,可参考点击我
使用redis事务时需要注意的地方
说到了事务,不得不再提醒大家一个很容易忽视的问题。拿stackoverflow网站上的一个经典问答来做例子:
redis.multi() current = redis.get('powerlevel') redis.set('powerlevel', current + 1) redis.exec()
其事务法是无法生效的,是因为redis在MULTI/EXEC代码块中,命令都会被delay,放入Queue中,而不会直接返回对应的值。即例子中的get将返回一个字符串“QUEUED”,从而set中无法实现自增。要完成这一切,请使用:
redis.watch('powerlevel') current = redis.get('powerlevel') redis.multi() redis.set('powerlevel', current + 1) redis.exec()
通过监听powerlevel key来实现数据的一致性。一旦该key被其他会话修改,这个事务即会失败。同时由于将get操作从MULTI块中提出,从而避免了无法获取数据的情况。
- spring-data-redis中的坑和误区
- Spring-data-redis在shiro中的实例
- spring-data-redis scan 中的cursor关闭
- redis:spring-data-redis
- spring data Redis 集成jedis操作 redis中的数据。
- 利用Spring-Data-Redis和Jedis操作Redis缓存
- spring data redis 操作redis 单机版和集群
- spring data redis防坑指南
- redis Jredis spring-data-redis
- spring data redis 操作redis
- Spring Data Redis (Redis Support)
- Spring Data Redis(Redis Transactions)
- Spring Data Redis(Redis Scripting)
- Spring Data Redis(Redis Cluster)
- Spring Data Redis(Redis Repositories)
- spring data redis 操作redis
- SPRING DATA - REDIS配置
- spring data redis试用
- Python字符串的encode与decode研究心得乱码问题解决方法
- Floyd算法求最短路问题
- 分布式锁的三种实现方式
- 一致性哈希原理与实现
- kidd风的IOS日志之触摸事件,手势识别,摇晃事件的详解
- spring-data-redis中的坑和误区
- DEA(数据包络分析)程序模板
- 为你的Android应用定制属于你的BaseActivity http://blog.csdn.net/jiahui524
- JVM学习笔记
- 将二叉树拆成链表 Flatten Binary Tree to Linked List
- 挨踢职场求生法则-----我在IT职场打滚超过15年了,从小小的程序员做到常务副总
- 判断int类型是否为空
- html个人模板
- 冒泡,选择,插入排序的效率比较