以业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如业务名:表名:id
ugc:video:1

拒绝bigkey

网络阻塞、redis超时(慢查询)、分片内存不均匀导致某些节点占用内存多

string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。

  1. 避免bigkey的方法,主要是对 bigkey 进行拆分,拆成多个 key,然后用MGET取回来,再在业务层做合并。


2. 删除bigkey(下面有)
一个包含200万个元素的list。
非字符串的bigkey,不要使用del删除,使用hscan、sscan、zscan方式渐进式删除,同时要注意防止bigkey过期时间自动删除问题(例如一个200万的zset设置1小时过期,会触发del操作,造成阻塞,而且该操作不会不出现在慢查询中(latency可查)),查找方法和删除方法

控制key的生命周期

redis不是垃圾桶,建议使用expire设置过期时间(条件允许可以打散过期时间,防止集中过期),不过期的数据重点关注idletime。

禁用命令

禁止线上使用keys、flushall、flushdb等,通过redis的rename机制禁掉命令,或者使用scan的方式渐进式处理。

不建议过多使用Redis事务功能

Redis的事务功能较弱(不支持回滚),而且集群版本(自研和官方)要求一次事务操作的key必须在一个slot上(可以使用hashtag功能解决)

删除bigkey

  • 下面操作可以使用pipeline加速。
  • redis 4.0已经支持key的异步删除,欢迎使用。

    1、Hash删除: hscan + hdel

    1. public void delBigHash(String host, int port, String password, String bigHashKey) {
    2. Jedis jedis = new Jedis(host, port);
    3. if (password != null && !"".equals(password)) {
    4. jedis.auth(password);
    5. }
    6. ScanParams scanParams = new ScanParams().count(100);
    7. String cursor = "0";
    8. do {
    9. ScanResult<Entry<String, String>> scanResult = jedis.hscan(bigHashKey, cursor, scanParams);
    10. List<Entry<String, String>> entryList = scanResult.getResult();
    11. if (entryList != null && !entryList.isEmpty()) {
    12. for (Entry<String, String> entry : entryList) {
    13. jedis.hdel(bigHashKey, entry.getKey());
    14. }
    15. }
    16. cursor = scanResult.getStringCursor();
    17. } while (!"0".equals(cursor));
    18. //删除bigkey
    19. jedis.del(bigHashKey);
    20. }

    2、List删除: ltrim

    1. public void delBigList(String host, int port, String password, String bigListKey) {
    2. Jedis jedis = new Jedis(host, port);
    3. if (password != null && !"".equals(password)) {
    4. jedis.auth(password);
    5. }
    6. long llen = jedis.llen(bigListKey);
    7. int counter = 0;
    8. int left = 100;
    9. while (counter < llen) {
    10. //每次从左侧截掉100个
    11. jedis.ltrim(bigListKey, left, llen);
    12. counter += left;
    13. }
    14. //最终删除key
    15. jedis.del(bigListKey);
    16. }

    3、Set删除: sscan + srem

    public void delBigSet(String host, int port, String password, String bigSetKey) {
      Jedis jedis = new Jedis(host, port);
      if (password != null && !"".equals(password)) {
          jedis.auth(password);
      }
      ScanParams scanParams = new ScanParams().count(100);
      String cursor = "0";
      do {
          ScanResult<String> scanResult = jedis.sscan(bigSetKey, cursor, scanParams);
          List<String> memberList = scanResult.getResult();
          if (memberList != null && !memberList.isEmpty()) {
              for (String member : memberList) {
                  jedis.srem(bigSetKey, member);
              }
          }
          cursor = scanResult.getStringCursor();
      } while (!"0".equals(cursor));
      //删除bigkey
      jedis.del(bigSetKey);
    }
    

    4、SortedSet删除: zscan + zrem

    public void delBigZset(String host, int port, String password, String bigZsetKey) {
      Jedis jedis = new Jedis(host, port);
      if (password != null && !"".equals(password)) {
          jedis.auth(password);
      }
      ScanParams scanParams = new ScanParams().count(100);
      String cursor = "0";
      do {
          ScanResult<Tuple> scanResult = jedis.zscan(bigZsetKey, cursor, scanParams);
          List<Tuple> tupleList = scanResult.getResult();
          if (tupleList != null && !tupleList.isEmpty()) {
              for (Tuple tuple : tupleList) {
                  jedis.zrem(bigZsetKey, tuple.getElement());
              }
          }
          cursor = scanResult.getStringCursor();
      } while (!"0".equals(cursor));
      //删除bigkey
      jedis.del(bigZsetKey);
    }