Code前端首页关于Code前端联系我们

民工兄Redis教程(二十三):阿里云开发规范

terry 2年前 (2023-09-26) 阅读数 101 #后端开发

本文介绍了使用阿里云Redis的开发规范,包括键值设计、命令使用、客户端使用、相关工具等。文章中,可以减少使用Redis进程带来的问题。

键值设计

键名设计

可读性和可管理性

以业务名称(或数据库名称)作为前缀(避免键冲突)并用冒号分隔,例如业务名称Name:id

ugc:video:1
简单性

在安全语义的假设下,控制密钥的长度。当key很多的时候,内存的占用就不能忽略了。例如:

user:{uid}:friends:messages:{mid}简化为u:{uid}
不要插入特殊字符

反对示例:插入空格、换行、单双引号等转义字符

value设计

拒绝bigkey(防止网卡传输速度变慢)

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

反例:包含200万个元素的list。

bigkey,不是字符串,不要用del删除,使用hscan、sscan、zscan逐渐删除,注意避免bigkey过期时间自动删除的问题(比如200万

zset会设置过期1小时,就会开始del操作,这会造成死锁,对于慢查询不会出现该操作(可以检查延迟))。搜索方法和删除方法

选择合适的数据类型。 ?控制key生命周期,redis不是垃圾桶。

设置过期时间,建议使用expire(如果条件允许,可以划分过期时间,防止集中过期)。对于未过期的数据,重点关注空闲时间。命令

使用命令

O(N) 来聚焦数字 N

例如,hgetall、lrange、smembers、zrange、sinter 等。它们并不是不能用,但是N的值需要明确。如果您有遍历需求,可以使用 hscan、sscan 或 zscan 代替。

禁用命令

禁用键、flushall、flushdb 等的使用。在线的。通过 redis 重命名机制禁用命令或使用扫描顺序处理它们。

合理使用所选

Redis多数据库较弱,用数字来区分。许多客户的支持很差。同时多数据库多业务其实都是单线程的,会造成干扰。

使用批处理操作提高效率

  • 原生命令:如mget、mset。
  • 非原生命令:您可以使用管道来提高效率。

但是要注意批量操作中检查元素的数量(比如500以内,这其实和元素的字节数有关)。

注意两者之间的区别:

  • Native 是原子操作,pipe 是非原子操作。
  • Pipeline 可以包装各种原生无法执行的命令。
  • 管道必须得到客户端和服务器的支持。

不建议过于频繁使用Redis事务功能)。

Redis集群版本对使用Lua有特殊要求

1。所有密钥都应通过 KEYS 字段。 redis.call/pcall 中调用的 Redis 命令且 key 位置必须是 KEYS 数组,否则直接返回错误,“-ERR bad lua script for redis cluster, all keys use by script should be pass using KEYSrn array”

2.所有按键必须在1个插槽中,否则会出错,“- ERR eval/evalsha命令按键必须在同一个插槽中”

monitor命令

使用monitor命令时,如有必要,请注意不要使用许久。

客户端使用

避免多个应用程序使用单个Redis实例

正面示例:拆分不相关的存储并将公共数据用作服务。

使用连接池数据库

可以有效控制连接,同时提高效率。标准使用:

Jedis jedis = null;
try {
    jedis = jedisPool.getResource();
    //具体的命令
    jedis.executeCommand()
} catch (Exception e) {
    logger.error("op key {} error: " + e.getMessage(), key, e);
} finally {
    //注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。
    if (jedis != null) 
        jedis.close();
}

阻断功能

高并发时,建议客户端添加断路器功能(如Netflix hystrix)

合理加密

必要时使用合理的密码和SSL加密(支持阿里云Redis)

淘汰策略

根据您的业务类型选择maxmemory-policy(最大内存淘汰策略),设置过期时间。

默认策略是 volatile-lru,即超过最大内存时,会使用 lru 算法淘汰过期的 key,保证过期的数据不被删除,但可能存在 OOM 的问题。

其他策略如下:

  • allkeys-lru:根据LRU算法删除key,无论数据是否设置了超时属性,直到释放足够的空间。
  • allkeys-random:随机删除所有键,直到释放足够的空间。
  • 易失性随机:随机删除过期的键,直到释放足够的空间。
  • volatile-ttl:根据键值对象的ttl属性删除最近过期的数据。如果没有,请返回驱逐策略。
  • discard:不会删除任何数据,所有写操作都会被拒绝,并返回客户端错误信息“(error) OOM command is not allowed while using memory”。目前,Redis 只会响应读操作。

相关工具

数据同步

redis之间的数据同步可以使用:redis-port

大键查找

Horedis大键查找‼内部实现使用monitor。建议短时间使用Facebook的redis-fain。阿里云Redis在内核层面解决了热键问题

删除bigkey

  • 以下操作可以使用管道加速。
  • Redis 4.0已经支持异步key删除,欢迎使用。

清除散列:hscan + hdel

public void delBigHash(String host, int port, String password, String bigHashKey) {
    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<Entry<String, String>> scanResult = jedis.hscan(bigHashKey, cursor, scanParams);
        List<Entry<String, String>> entryList = scanResult.getResult();
        if (entryList != null && !entryList.isEmpty()) {
            for (Entry<String, String> entry : entryList) {
                jedis.hdel(bigHashKey, entry.getKey());
            }
        }
        cursor = scanResult.getStringCursor();
    } while (!"0".equals(cursor));
    //删除bigkey
    jedis.del(bigHashKey);
}

清除列表:ltrim

public void delBigList(String host, int port, String password, String bigListKey) {
    Jedis jedis = new Jedis(host, port);
    if (password != null && !"".equals(password)) {
        jedis.auth(password);
    }
    long llen = jedis.llen(bigListKey);
    int counter = 0;
    int left = 100;
    while (counter < llen) {
        //每次从左侧截掉100个
        jedis.ltrim(bigListKey, left, llen);
        counter += left;
    }
    //最终删除key
    jedis.del(bigListKey);
}

清除集合: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);
}

清除排序文件:https://zscan + zourcel ky2xn

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门