YCSB测试Redis方法总结

2022-05-12 10:53:21 浏览数 (1)

https://github.com/brianfrankcooper/YCSB/tree/master/redis

YCSB可以模拟真实业务场景进行压力测试,有一定真实性。(redis-benchmark和memiter没有这种功能)

环境部署

yum install java

yum install maven

maven需要安装他需要的版本,否则编译失败切记。(编译失败就需要换版本,需要的版本在报错信息中可以看到)

git clone http://github.com/brianfrankcooper/YCSB.git cd YCSB mvn -pl site.ycsb:redis-binding -am clean package

测试命令

测试按照下面链接建议的顺序执行

https://github.com/brianfrankcooper/YCSB/wiki/Core-Workloads

具体命令如下,注意这里是绑核单机测试,按需要更改。(redis启动绑了0核)

代码语言:javascript复制
server_ip=127.0.0.1
port=7001
recordcount=100000000
operationcount=20000000
command_group=string
fieldcount=1
fieldlength=100
threads=32



# load 灌104字符
taskset -c 2 ./bin/ycsb load redis -s -P workloads/workloada -p "redis.host=${server_ip}" -p  "redis.port=${port}" -p "recordcount=${recordcount}" -p "operationcount=${operationcount}" -p "redis.timeout=30000" -p "redis.command_group=${command_group}" -p "fieldcount=${fieldcount}" -p "fieldlength=${fieldlength}" -threads ${threads}
# a
taskset -c 2 ./bin/ycsb run redis -s -P workloads/workloada -p "redis.host=${server_ip}" -p  "redis.port=${port}" -p "recordcount=${recordcount}" -p "operationcount=${operationcount}" -p "redis.timeout=30000" -p "redis.command_group=${command_group}" -p "fieldcount=${fieldcount}" -p "fieldlength=${fieldlength}" -threads ${threads}
# b
taskset -c 2 ./bin/ycsb run redis -s -P workloads/workloadb -p "redis.host=${server_ip}" -p  "redis.port=${port}" -p "recordcount=${recordcount}" -p "operationcount=${operationcount}" -p "redis.timeout=30000" -p "redis.command_group=${command_group}" -p "fieldcount=${fieldcount}" -p "fieldlength=${fieldlength}" -threads ${threads}
# c
taskset -c 2 ./bin/ycsb run redis -s -P workloads/workloadc -p "redis.host=${server_ip}" -p  "redis.port=${port}" -p "recordcount=${recordcount}" -p "operationcount=${operationcount}" -p "redis.timeout=30000" -p "redis.command_group=${command_group}" -p "fieldcount=${fieldcount}" -p "fieldlength=${fieldlength}" -threads ${threads}
# f
taskset -c 2 ./bin/ycsb run redis -s -P workloads/workloadf -p "redis.host=${server_ip}" -p  "redis.port=${port}" -p "recordcount=${recordcount}" -p "operationcount=${operationcount}" -p "redis.timeout=30000" -p "redis.command_group=${command_group}" -p "fieldcount=${fieldcount}" -p "fieldlength=${fieldlength}" -threads ${threads}
# d
taskset -c 2 ./bin/ycsb run redis -s -P workloads/workloadd -p "redis.host=${server_ip}" -p  "redis.port=${port}" -p "recordcount=${recordcount}" -p "operationcount=${operationcount}" -p "redis.timeout=30000" -p "redis.command_group=${command_group}" -p "fieldcount=${fieldcount}" -p "fieldlength=${fieldlength}" -threads ${threads}
# flushdb
# e
taskset -c 2 ./bin/ycsb load redis -s -P workloads/workloade -p "redis.host=${server_ip}" -p  "redis.port=${port}" -p "recordcount=${recordcount}" -p "operationcount=${operationcount}" -p "redis.timeout=30000" -p "redis.command_group=${command_group}" -p "fieldcount=${fieldcount}" -p "fieldlength=${fieldlength}" -threads ${threads}
taskset -c 2 ./bin/ycsb run redis -s -P workloads/workloade -p "redis.host=${server_ip}" -p  "redis.port=${port}" -p "recordcount=${recordcount}" -p "operationcount=${operationcount}" -p "redis.timeout=30000" -p "redis.command_group=${command_group}" -p "fieldcount=${fieldcount}" -p "fieldlength=${fieldlength}" -threads ${threads}

默认是hash作为values测试

里面有下载链接

代码语言:javascript复制
/**
 * Copyright (c) 2012 YCSB contributors. All rights reserved.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You
 * may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License. See accompanying
 * LICENSE file.
 * <p>
 * Redis client binding for YCSB.
 * <p>
 * All YCSB records are mapped to a Redis *hash field*.  For scanning
 * operations, all keys are saved (by an arbitrary hash) in a sorted set.
 * <p>
 * Redis client binding for YCSB.
 * <p>
 * All YCSB records are mapped to a Redis *hash field*.  For scanning
 * operations, all keys are saved (by an arbitrary hash) in a sorted set.
 */

/**
 * Redis client binding for YCSB.
 *
 * All YCSB records are mapped to a Redis *hash field*.  For scanning
 * operations, all keys are saved (by an arbitrary hash) in a sorted set.
 */

package site.ycsb.db;

import redis.clients.jedis.Tuple;
import site.ycsb.ByteArrayByteIterator;
import site.ycsb.ByteIterator;
import site.ycsb.DB;
import site.ycsb.DBException;
import site.ycsb.Status;
import redis.clients.jedis.BasicCommands;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisCommands;
import redis.clients.jedis.Protocol;
import site.ycsb.StringByteIterator;

import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * YCSB binding for <a href="http://redis.io/">Redis</a>.
 *
 * See {@code redis/README.md} for details.
 */
public class RedisClient extends DB {

  public static final String HOST_PROPERTY = "redis.host";
  public static final String PORT_PROPERTY = "redis.port";
  public static final String PASSWORD_PROPERTY = "redis.password";
  public static final String CLUSTER_PROPERTY = "redis.cluster";
  public static final String COMMAND_PROPERTY = "redis.command_group";
  public static final String TIMEOUT_PROPERTY = "redis.timeout";
  public static final String DEBUG_PROPERTY = "redis.debug";
  public static final String RANGE_PROPERTY = "redis.range";

  public static final String FIELD_COUNT = "fieldcount";
  public static final String FIELD_LENGTH = "fieldlength";

  /** the default command group is "string", it can be {string, hash}. */
  public enum CommandType {
    COMMAND_STRING,
    COMMAND_HASH,
    COMMAND_LIST,
    COMMAND_SET,
    COMMAND_ZSET
  }

  private static final String STRING_COMMAND = "string";
  private static final String HASH_COMMAND = "hash";
  private static final String LIST_COMMAD = "list";
  private static final String SET_COMMAND = "set";
  private static final String ZSET_COMMAND = "zset";

  private static final String RETURN_OK = "OK";

  private static final Set<String> COMMAND_TABLE = new HashSet<String>() {
    {
      add(STRING_COMMAND);
      add(HASH_COMMAND);
      add(LIST_COMMAD);
      add(SET_COMMAND);
      add(ZSET_COMMAND);
    }
  };

  private JedisCommands jedis;
  private String commandGroup = STRING_COMMAND;
  private CommandType commandType = CommandType.COMMAND_STRING;
  private int fieldCount = 0;
  private int fieldLength = 0;
  private int timeout = 2000;
  private Boolean debugMode = false;
  private int[] range = null;

  private static CommandType convertToCommandType(String command) throws DBException {
    if (command.equals(STRING_COMMAND)) {
      return CommandType.COMMAND_STRING;
    } else if (command.equals(HASH_COMMAND)) {
      return CommandType.COMMAND_HASH;
    } else if (command.equals(LIST_COMMAD)) {
      return CommandType.COMMAND_LIST;
    } else if (command.equals(SET_COMMAND)) {
      return CommandType.COMMAND_SET;
    } else if (command.equals(ZSET_COMMAND)) {
      return CommandType.COMMAND_ZSET;
    } else {
      throw new DBException(String.format("command %s is invalid", command));
    }
  }

  @Override
  public void init() throws DBException {
    Properties props = getProperties();
    int port;

    String debugModeString = props.getProperty(DEBUG_PROPERTY);
    if (debugModeString != null) {
      debugMode = Boolean.parseBoolean(debugModeString);
    }

    String rangeString = props.getProperty(RANGE_PROPERTY);
    if (rangeString != null) {
      range = new int[2];
      String[] r = rangeString.split(",");
      assert r.length == 2;
      range[0] = Integer.parseInt(r[0]);
      range[1] = Integer.parseInt(r[1]);
    }

    String fieldCountString = props.getProperty(FIELD_COUNT);
    if (fieldCountString != null) {
      fieldCount = Integer.parseInt(fieldCountString);
    }

    String fieldLengthString = props.getProperty(FIELD_LENGTH);
    if (fieldLengthString != null) {
      fieldLength = Integer.parseInt(fieldLengthString);
    }

    String portString = props.getProperty(PORT_PROPERTY);
    if (portString != null) {
      port = Integer.parseInt(portString);
    } else {
      port = Protocol.DEFAULT_PORT;
    }
    String host = props.getProperty(HOST_PROPERTY);

    String timeoutString = props.getProperty(TIMEOUT_PROPERTY);
    if (timeoutString != null) {
      timeout = Integer.parseInt(timeoutString);
    }

    boolean clusterEnabled = Boolean.parseBoolean(props.getProperty(CLUSTER_PROPERTY));
    if (clusterEnabled) {
      Set<HostAndPort> jedisClusterNodes = new HashSet<>();
      jedisClusterNodes.add(new HostAndPort(host, port));
      jedis = new JedisCluster(jedisClusterNodes, timeout);
    } else {
      jedis = new Jedis(host, port, timeout);
      ((Jedis) jedis).connect();
    }

    String password = props.getProperty(PASSWORD_PROPERTY);
    if (password != null) {
      ((BasicCommands) jedis).auth(password);
    }

    commandGroup = props.getProperty(COMMAND_PROPERTY, STRING_COMMAND);
    if (commandGroup == null) {
      throw new DBException(String.format("the command group is illegal"));
    }

    if (!COMMAND_TABLE.contains(commandGroup)) {
      System.out.println("the command group should be in "   COMMAND_TABLE);
      throw new DBException(String.format("the command group is invalid, commandGroup: %s", commandGroup));
    }

    commandType = convertToCommandType(commandGroup);

    if (debugMode) {
      System.out.println(String.format("Properties: %s", props.toString()));
    }
  }

  @Override
  public void cleanup() throws DBException {
    try {
      ((Closeable) jedis).close();
    } catch (IOException e) {
      throw new DBException("Closing connection failed.");
    }
  }

  // XXX jedis.select(int index) to switch to `table`

  @Override
  public Status read(String table, String key, Set<String> fields,
                     Map<String, ByteIterator> result) {
    assert fields == null;
    switch (commandType) {
    case COMMAND_STRING:
      if (fields == null) {
        String fieldValue = jedis.get(key);

        if (debugMode) {
          System.out.println(String.format("get key:%s fields:%s, vals:%s", key, fields, fieldValue));
        }
        if (fieldValue != null && !fieldValue.isEmpty()) {
          return Status.OK;
        }
        return Status.NOT_FOUND;
      }
      break;
    case COMMAND_HASH:
      if (fields == null) {
        // hgetAll
        Map<String, String> values = jedis.hgetAll(key);

        if (debugMode) {
          System.out.println(String.format("hgetAll key:%s, vals:%s", key, values));
        }
        if (values != null && !values.isEmpty()) {
          if (values.size() == fieldCount) {
            return Status.OK;
          } else {
            return Status.ERROR;
          }
        }
        return Status.NOT_FOUND;
      }
      break;
    case COMMAND_LIST:
      if (fields == null) {
        // lrange
        int startIdx = 0;
        int endIdx = -1;
        if (range != null) {
          startIdx = range[0];
          endIdx = range[1];
        }
        List<String> values = jedis.lrange(key, startIdx, endIdx);

        if (debugMode) {
          System.out.println(String.format("lrange key:%s start:%d end:%d, vals:%s", key, startIdx, endIdx, values));
        }
        if (values != null && !values.isEmpty()) {
          return Status.OK;
        }
        return Status.NOT_FOUND;
      }
      break;
    case COMMAND_SET:
      if (fields == null) {
        // smembers
        Set<String> members = jedis.smembers(key);

        if (debugMode) {
          System.out.println(String.format("smembers key:%s, vals:%s", key, members));
        }
        if (members != null && !members.isEmpty()) {
          return Status.OK;
        }
        return Status.NOT_FOUND;
      }
      break;
    case COMMAND_ZSET:
      if (fields == null) {
        // zrangeWithScores
        int startIdx = 0;
        int endIdx = -1;
        if (range != null) {
          startIdx = range[0];
          endIdx = range[1];
        }
        Set<Tuple> members = jedis.zrangeWithScores(key, startIdx, endIdx);

        if (debugMode) {
          System.out.println(String.format("zrangeWithScores key:%s start:%d end:%d, vals:%s",
              key, startIdx, endIdx, members));
        }
        if (members != null && !members.isEmpty()) {
          return Status.OK;
        }
        return Status.NOT_FOUND;
      }
      break;
    default:
    }

    return Status.ERROR;
  }

  public Status insertHelper(String table, String key,
                            Map<String, ByteIterator> values, boolean update) {
    switch (commandType) {
    case COMMAND_STRING:
      assert values.size() == 1;
        // set
      Map.Entry<String, ByteIterator> field0 = values.entrySet().iterator().next();
      String field0Value = field0.getValue().toString();
      if (debugMode) {
        System.out.println(String.format("set key:%s val:%s", key, field0Value));
      }

      String stringRet = jedis.set(key, field0Value);
      if (stringRet.equals(RETURN_OK)) {
        return Status.OK;
      }
      break;
    case COMMAND_HASH:
      // hmset
      Map<String, String> fieldValues = StringByteIterator.getStringMap(values);
      if (debugMode) {
        System.out.println(String.format("hmset key:%s fields:%s", key, fieldValues));
      }

      if (update) {
        assert values.size() == 1;
      }

      String hashRet = jedis.hmset(key, fieldValues);
      if (hashRet.equals(RETURN_OK)) {
        return Status.OK;
      }
      break;
    case COMMAND_LIST:
      // lpush
      String[] listValues = getStringArrayFromMapValues(values);
      if (debugMode) {
        System.out.println(String.format("lpush key:%s val:%s", key, Arrays.toString(listValues)));
      }

      Long listRet = jedis.lpush(key, listValues);

      if (update) {
        assert(values.size() == 1);
        String popValue = jedis.rpop(key);
        if (popValue == null) {
          System.out.println("rpop error");
          return Status.ERROR;
        }
      }
      return Status.OK;
    case COMMAND_SET:
      // sadd
      String[] members = getStringArrayFromMapValues(values);
      if (debugMode) {
        System.out.println(String.format("sadd key:%s members:%s", key, Arrays.toString(members)));
      }

      long addNumSet = jedis.sadd(key, members);

      if (update) {
        assert members.length == 1;
        if (addNumSet == 1) {
          String popMember = jedis.spop(key);
          if (popMember == null) {
            return Status.ERROR;
          }
        } else if (addNumSet > 1) {
          System.err.println("Unexpect error");
          return Status.ERROR;
        }
      }

      return Status.OK;
    case COMMAND_ZSET:
      // zadd
      Map<String, Double> scoreMembers = getStringScoreMapFromMapValues(values);
      if (debugMode) {
        System.out.println(String.format("zadd key:%s scoreMembers:%s", key, scoreMembers));
      }

      long addNumZset = jedis.zadd(key, scoreMembers);

      if (update) {
        assert scoreMembers.size() == 1;
        if (addNumZset == 1) {
          long removeNum = jedis.zremrangeByRank(key, fieldCount, fieldCount);
          if (removeNum != 1) {
            return Status.ERROR;
          }
        } else if (addNumZset > 1) {
          System.err.println("Unexpect error");
          return Status.ERROR;
        }
      }

      return Status.OK;
    default:
    }

    return Status.ERROR;
  }

  @Override
  public Status insert(String table, String key,
                       Map<String, ByteIterator> values) {
    return insertHelper(table, key, values, false);
  }

  @Override
  public Status delete(String table, String key) {
    return jedis.del(key) == 0 ? Status.ERROR : Status.OK;
  }

  @Override
  public Status update(String table, String key,
                       Map<String, ByteIterator> values) {
    return insertHelper(table, key, values, true);
  }

  @Override
  public Status scan(String table, String startkey, int recordcount,
                     Set<String> fields, Vector<HashMap<String, ByteIterator>> result) {
    return Status.NOT_IMPLEMENTED;

  }

  /*
   * Calculate a hash for a key to store it in an index. The actual return value
   * of this function is not interesting -- it primarily needs to be fast and
   * scattered along the whole space of doubles. In a real world scenario one
   * would probably use the ASCII values of the keys.
   */
  private static int hash32(String key) {
    return key.hashCode();
  }

  private static String[] getStringArrayFromMapValues(Map<String, ByteIterator> m) {
    String[] ret = new String[m.size()];
    int i = 0;
    for (Map.Entry<String, ByteIterator> entry : m.entrySet()) {
      ret[i] = entry.getValue().toString();
        i;
    }
    return ret;
  }

  private static Map<String, Double> getStringScoreMapFromMapValues(Map<String, ByteIterator> m) {
    Map<String, Double> ret = new HashMap<>(m.size());
    int i = 0;
    for (Map.Entry<String, ByteIterator> entry : m.entrySet()) {
      String member = entry.getValue().toString();
      Double score = Math.random();
      ret.put(member, score);
    }
    return ret;
  }

  private String serializeValues(final Map<String, ByteIterator> values) throws IOException {
    try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
      final ByteBuffer buf = ByteBuffer.allocate(4);

      for (final Map.Entry<String, ByteIterator> value : values.entrySet()) {
        // map kv to record[key_len(4)   key   value_len(4)   value]
        final byte[] keyBytes = value.getKey().getBytes(UTF_8);
        final byte[] valueBytes = value.getValue().toArray();

        buf.putInt(keyBytes.length);
        baos.write(buf.array());
        baos.write(keyBytes);

        buf.clear();

        buf.putInt(valueBytes.length);
        baos.write(buf.array());
        baos.write(valueBytes);

        buf.clear();
      }
      return new String(buf.array(), UTF_8);
    }
  }

  private Map<String, ByteIterator> deserializeValues(final String valueString, final Set<String> fields,
                                                      final Map<String, ByteIterator> result) {
    byte[] values = valueString.getBytes(UTF_8);
    final ByteBuffer buf = ByteBuffer.allocate(4);

    int offset = 0;
    while (offset < values.length) {
      buf.put(values, offset, 4);
      buf.flip();
      final int keyLen = buf.getInt();
      buf.clear();
      offset  = 4;

      final String key = new String(values, offset, keyLen, UTF_8);
      offset  = keyLen;

      buf.put(values, offset, 4);
      buf.flip();
      final int valueLen = buf.getInt();
      buf.clear();
      offset  = 4;

      if (fields == null || fields.contains(key)) {
        result.put(key, new ByteArrayByteIterator(values, offset, valueLen));
      }

      offset  = valueLen;
    }

    return result;
  }
}

0 人点赞