• 【转载】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带来了极大的方便。

  • 相关阅读:
    传统金融和互联网金融
    集团培训
    Javascript和JQuery之间的联系
    this和$(this)区别
    原生JavaScript支持6种方式获取元素
    绩效考核
    web服务端安全之分布式拒绝服务攻击
    web服务端安全之暴力破解
    web服务端安全之权限漏洞
    web服务端安全之文件上传漏洞
  • 原文地址:https://www.cnblogs.com/mr-yang-localhost/p/8461289.html
Copyright © 2020-2023  润新知