转载redis优化

Posted Mr.yang.localhost

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了转载redis优化相关的知识,希望对你有一定的参考价值。

原文链接

批量操作优化:

  • 在使用redis的时候,客户端通过socket向redis服务端发起请求后,等待服务端的返回结果。
  • 客户端请求服务器一次就发送一个报文 -> 等待服务端的返回 -> 关闭连接
  • 如果是100个请求、1000个请求,那就得请求100次、1000次
  • 所以使用多个请求的时候使用管道来操作(如果管道打包的命令太多占用的内存也会越大,适量)
  • 以下是使用3种方式进行的测试(循环写入1000次):

    public static void main(String[] args) {
    
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++) {
            template.opsForHash().put("user1", "status" + i, "value" + i);
        }
        System.out.println(System.currentTimeMillis() - start);
    
        start = System.currentTimeMillis();
        final byte[] rawKey = rawKey("user2");
        template.execute(new RedisCallback<Object>() {
    
            @Override
            public Object doInRedis(RedisConnection connection)
                    throws DataAccessException {
                for (int i = 0; i < 1000; i++) {
                    final byte[] rawHashKey = rawHashKey("status" + i);
                    final byte[] rawHashValue = rawHashValue("value" + i);
    
                    connection.hSet(rawKey, rawHashKey, rawHashValue);
                }
                return null;
            }
        });
        System.out.println(System.currentTimeMillis() - start);
    
        start = System.currentTimeMillis();
        final byte[] rawKey2 = rawKey("user3");
        template.execute(new RedisCallback<Object>() {
    
            @Override
            public Object doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.openPipeline();
                for (int i = 0; i < 1000; i++) {
                    final byte[] rawHashKey = rawHashKey("status" + i);
                    final byte[] rawHashValue = rawHashValue("value" + i);
    
                    connection.hSet(rawKey2, rawHashKey, rawHashValue);
                }
                return connection.closePipeline();
            }
        });
        System.out.println(System.currentTimeMillis() - start);
    }
  • 请求结果

    20:54:51.653 [main] DEBUG o.s.d.r.core.RedisConnectionUtils - Opening RedisConnection
    20:54:51.693 [main] DEBUG o.s.d.r.core.RedisConnectionUtils - Closing Redis Connection
    42283
    20:54:51.695 [main] DEBUG o.s.d.r.core.RedisConnectionUtils - Opening RedisConnection
    20:55:33.922 [main] DEBUG o.s.d.r.core.RedisConnectionUtils - Closing Redis Connection
    42228
    20:55:33.923 [main] DEBUG o.s.d.r.core.RedisConnectionUtils - Opening RedisConnection
    20:55:34.058 [main] DEBUG o.s.d.r.core.RedisConnectionUtils - Closing Redis Connection
    136
  • 可以看出在使用管道打包发送请求所用的时间不到1s。而发送1000次请求所用的时间达到了42s。

从请求结果看到多次 Opening RedisConnection 和 Closing Redis Connection 的日志.

阅读源代码我们可以发现我们对redis的所有操作都是通过回调execute函数执行的,其代码如下:

public <T> T execute(RedisCallback<T> action, boolean exposeConnection) {  
    return execute(action, exposeConnection, false);  
}  
// execute实现如下:  
// org.springframework.data.redis.core.RedisTemplate<K, V> --- 最终实现  
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {  
    Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");  
    Assert.notNull(action, "Callback object must not be null");  
    RedisConnectionFactory factory = getConnectionFactory();  
    RedisConnection conn = null;  
    try {  
        if (enableTransactionSupport) {  
            // only bind resources in case of potential transaction synchronization  
            conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);  
        } else {  
            conn = RedisConnectionUtils.getConnection(factory);  
        }  
        boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);  
        RedisConnection connToUse = preProcessConnection(conn, existingConnection);  
        boolean pipelineStatus = connToUse.isPipelined();  
        if (pipeline && !pipelineStatus) {  
            connToUse.openPipeline();  
        }  
        RedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse));  
        T result = action.doInRedis(connToExpose);  
        // close pipeline  
        if (pipeline && !pipelineStatus) {  
            connToUse.closePipeline();  
        }  
        // TODO: any other connection processing?  
        return postProcessResult(result, connToUse, existingConnection);  
    } finally {  
        if (!enableTransactionSupport) {  
            RedisConnectionUtils.releaseConnection(conn, factory);  
        }  
    }  
}  

这里面每次执行action.doInRedis(connToExpose)前都要调用RedisConnectionUtils.getConnection(factory);获得一个连接,进入RedisConnnectionUtils类中,getConnection(factory)最终调用的是doGetConnection(factory, true, false, enableTranactionSupport)这个函数。这个函数我们可以看下api文档,发现实际上并不是真的创建一个新的redis连接,它只是在connectFactory中获取一个连接,也就是从连接池中取出一个连接。当然如果connectFactory没有连接可用,此时如果allowCreate=true便会创建出一个新的连接,并且加入到connectFactory中。

基本上可以确定真实的情况是spring-data-redis已经帮我们封装了连接池管理,我们只需要调用一系列操作函数即可,这给操作redis带来了极大的方便。

以上是关于转载redis优化的主要内容,如果未能解决你的问题,请参考以下文章

精心收集的 48 个 JavaScript 代码片段,仅需 30 秒就可理解!(转载)

Redis单机主从高可用性优化

使用 C++ 反转句子中的每个单词需要对我的代码片段进行代码优化

如何优化C ++代码的以下片段 - 卷中的零交叉

从JVM的角度看JAVA代码--代码优化

京东科技Redis跨数据中心双向同步优化实践