Redis evalsha 可能返回的错误类型

 更新时间:2026年07月29日 09:57:57   作者:宁小法先森︿( ̄︶ ̄)︿  
本文总结了RedisService::center()->evalsha()方法执行时可能遇到的错误类型及处理方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

RedisService::center()->evalsha($hash, 1, ...$params);

这行代码执行时可能返回的报错信息包括:

🔍 Redis evalsha 可能返回的错误类型

1. NOSCRIPT 错误(最常见)

NOSCRIPT No matching script. Please use EVAL.

原因:Redis服务器上没有缓存这个SHA1哈希对应的Lua脚本。

2. Lua脚本执行错误

ERR Error running script (call to f_xxxxxxxxx): @user_script:行号: 错误描述

示例:

ERR Error running script (call to f_a1b2c3d4): @user_script:5: Attempt to call field 'nonexistent' (a nil value)

3. 参数数量错误

ERR wrong number of arguments for 'evalsha' command

原因:传递给evalsha的参数数量不正确。

4. 键名参数错误

ERR value is not an integer or out of range

原因:第二个参数(keys的数量)不是整数或范围不正确。

5. 数据类型错误

ERR Error running script (call to f_xxxxxxxxx): @user_script:行号: user_script:行号: 数据类型错误

6. 内存不足错误

OOM command not allowed when used memory > 'maxmemory'

7. 连接错误

Connection refused
Connection timeout

📊 针对您代码的具体错误场景

场景1:$hash无效

// 如果 $hash 为空、null 或无效的SHA1
$hash = null;  // 或 ''
$result = RedisService::center()->evalsha($hash, 1, ...$params);
// 错误:-ERR value is not a valid SHA-1 hex digest string

场景2:参数数量不匹配

// Lua脚本期望1个key,但传入了错误的参数
$params = ['key1', 'value1', 'value2'];  // 参数数量不对
$result = RedisService::center()->evalsha($hash, 1, ...$params);
// 可能错误:ERR wrong number of arguments

场景3:Redis连接问题

// Redis服务不可用
$result = RedisService::center()->evalsha($hash, 1, ...$params);
// 错误:Predis\Connection\ConnectionException
// 或:Connection refused

场景4:脚本执行超时

// Lua脚本执行时间过长
$result = RedisService::center()->evalsha($hash, 1, ...$params);
// 错误:BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.

🔧 完整的错误处理改进

public static function exec($command, $params)
{
    $hash = self::loadScript($command);
    
    // 验证hash有效性
    if (empty($hash) || !preg_match('/^[a-f0-9]{40}$/i', $hash)) {
        Log::error('Invalid SHA1 hash for Lua script', [
            'command' => $command,
            'hash' => $hash,
            'params' => $params
        ]);
        
        return [
            'current_amount' => 0,
            'error' => 'INVALID_SCRIPT_HASH',
            'message' => 'Script hash is invalid'
        ];
    }
    
    // 验证参数
    if (!is_array($params) || empty($params)) {
        Log::warning('Empty parameters for Lua script', [
            'command' => $command,
            'hash' => $hash
        ]);
    }
    
    try {
        $begin_time = microtime(true);
        
        // 执行evalsha
        $result = RedisService::center()->evalsha($hash, 1, ...$params);
        
        self::timeCost($command, $params, $begin_time);
        
        // 验证返回结果
        if (!is_array($result) || count($result) < 8) {
            Log::error('Invalid Lua script return format', [
                'command' => $command,
                'hash' => $hash,
                'result' => $result,
                'result_type' => gettype($result)
            ]);
            
            return [
                'current_amount' => 0,
                'error' => 'INVALID_RESULT_FORMAT',
                'message' => 'Lua script returned invalid format'
            ];
        }
        
        // 解析返回的数组
        return [
            'current_amount' => $result[0] ?? 0,
            'lower_limit' => $result[1] ?? 0,
            'upper_limit' => $result[2] ?? 0,
            'round' => $result[3] ?? 0,
            'round_changed' => (bool)($result[4] ?? 0),
            'actual_increment' => $result[5] ?? 0,
            'operate_state' => $result[6] ?? 0,
            'current_timestamp' => $result[7] ?? time(),
            'success' => true
        ];
        
    } catch (\Predis\Response\ServerException $e) {
        // Redis服务器错误(Lua脚本错误、NOSCRIPT等)
        return self::parseRedisServerException($command, $params, $e);
        
    } catch (\Predis\Connection\ConnectionException $e) {
        // 连接错误
        return self::parseConnectionException($command, $params, $e);
        
    } catch (\Exception $e) {
        // 其他异常
        return self::parseGenericException($command, $params, $e);
    }
}

/**
 * 解析Redis服务器异常
 */
private static function parseRedisServerException($command, $params, $exception)
{
    $message = $exception->getMessage();
    
    // 分类处理不同的Redis错误
    if (strpos($message, 'NOSCRIPT') !== false) {
        Log::warning('Redis script not loaded', [
            'command' => $command,
            'error' => 'NOSCRIPT',
            'message' => $message
        ]);
        
        // 清除缓存,触发重新加载
        self::clearScriptCache($command);
        
        return [
            'current_amount' => 0,
            'error' => 'NOSCRIPT',
            'message' => 'Script not loaded in Redis',
            'recoverable' => true
        ];
        
    } elseif (strpos($message, 'user_script:') !== false) {
        // Lua脚本执行错误
        Log::error('Lua script execution error', [
            'command' => $command,
            'error' => 'LUA_EXECUTION',
            'message' => $message
        ]);
        
        return [
            'current_amount' => 0,
            'error' => 'LUA_EXECUTION_ERROR',
            'message' => 'Script execution failed'
        ];
        
    } elseif (strpos($message, 'wrong number of arguments') !== false) {
        // 参数数量错误
        Log::error('Wrong number of arguments for Lua script', [
            'command' => $command,
            'params' => $params,
            'error' => 'WRONG_ARG_COUNT',
            'message' => $message
        ]);
        
        return [
            'current_amount' => 0,
            'error' => 'INVALID_ARGUMENTS',
            'message' => 'Wrong number of arguments'
        ];
        
    } elseif (strpos($message, 'OOM') !== false) {
        // 内存不足
        Log::critical('Redis out of memory', [
            'command' => $command,
            'error' => 'OUT_OF_MEMORY',
            'message' => $message
        ]);
        
        return [
            'current_amount' => 0,
            'error' => 'REDIS_OOM',
            'message' => 'Redis out of memory'
        ];
    }
    
    // 其他服务器错误
    return [
        'current_amount' => 0,
        'error' => 'REDIS_SERVER_ERROR',
        'message' => $message
    ];
}

/**
 * 解析连接异常
 */
private static function parseConnectionException($command, $params, $exception)
{
    $message = $exception->getMessage();
    
    if (strpos($message, 'Connection refused') !== false) {
        Log::critical('Redis connection refused', [
            'command' => $command,
            'error' => 'CONNECTION_REFUSED'
        ]);
        
    } elseif (strpos($message, 'Connection timeout') !== false) {
        Log::warning('Redis connection timeout', [
            'command' => $command,
            'error' => 'CONNECTION_TIMEOUT'
        ]);
    }
    
    return [
        'current_amount' => 0,
        'error' => 'CONNECTION_ERROR',
        'message' => 'Redis connection failed',
        'recoverable' => true
    ];
}

📈 错误分类和处理策略

错误类型

示例消息

严重程度

处理策略

是否可恢复

NOSCRIPT​

NOSCRIPT No matching script

清除缓存,重新加载脚本

✅ 是

Lua执行错误​

@user_script:5: nil value

记录日志,告警,修复脚本

❌ 否

参数错误​

wrong number of arguments

验证参数,修复调用

✅ 是

连接错误​

Connection refused

严重

重试,检查Redis服务

✅ 是

内存错误​

OOM command not allowed

严重

扩容,清理内存

⚠️ 可能

超时错误​

BUSY Redis is busy

增加超时时间,优化脚本

✅ 是

🎯 针对您代码的建议

1. 添加参数验证

// 在执行前验证参数
if (count($params) < 2) {
    throw new \InvalidArgumentException('Insufficient parameters for Lua script');
}

2. 实现重试机制

public static function execWithRetry($command, $params, $maxRetries = 2)
{
    $attempts = 0;
    $lastError = null;
    
    while ($attempts <= $maxRetries) {
        try {
            return self::exec($command, $params);
        } catch (\Exception $e) {
            $lastError = $e;
            $attempts++;
            
            // 只有可恢复的错误才重试
            if (self::isRecoverableError($e)) {
                usleep(100000 * $attempts); // 指数退避
                continue;
            }
            
            break;
        }
    }
    
    return self::parseException($command, $params, $lastError->getMessage());
}

3. 添加监控指标

// 记录错误统计
private static function recordErrorMetrics($errorType)
{
    // 使用StatsD、Prometheus等记录
    Metrics::increment("redis.lua.errors.{$errorType}");
}

💡 总结

evalsha可能返回的错误主要包括:

  • 脚本缓存错误:NOSCRIPT(最常见)
  • 脚本执行错误:Lua语法或运行时错误
  • 参数错误:参数数量或类型不正确
  • 连接错误:Redis服务不可用
  • 资源错误:内存不足、执行超时

在您的代码中,这些错误会被 try-catch捕获,然后调用 parseException方法处理。建议根据错误类型实现不同的处理策略,特别是对于可恢复的错误(如NOSCRIPT)应该实现自动恢复机制。

到此这篇关于Redis evalsha 可能返回的错误类型的文章就介绍到这了,更多相关Redis evalsha 错误内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 解读缓存db redis local的取舍之道

    解读缓存db redis local的取舍之道

    这篇文章主要介绍了解读缓存db redis local的取舍之道,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • Redis过期数据的删除策略详解

    Redis过期数据的删除策略详解

    Redis 是一个kv型数据库,我们所有的数据都是存放在内存中的,但是内存是有大小限制的,不可能无限制的增量,这篇文章主要介绍了Redis过期数据的删除策略,需要的朋友可以参考下
    2023-08-08
  • redis延迟双删策略示例讲解

    redis延迟双删策略示例讲解

    延迟是指当前请求逻辑处理延时,而不是当前线程或进程睡眠延迟,下面这篇文章主要给大家介绍了关于redis延迟双删策略的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-04-04
  • Redis的11种Web应用场景简介

    Redis的11种Web应用场景简介

    一些Redis原语命令比如LPUSH、LTRIM和 LREM等等能够用来帮助开发者完成需要的任务——这些任务在传统的数据库存储中非常困难或缓慢。这是一篇非常有用并且实际的文章。那么要如何在你的框架中完成这些任务呢?
    2015-09-09
  • 缓存替换策略及应用(以Redis、InnoDB为例)

    缓存替换策略及应用(以Redis、InnoDB为例)

    本文以Redis、InnoDB为例给大家讲解缓存替换策略及应用,本文给大家提到五种置换策略,通过实例代码给大家介绍的非常详细,需要的朋友参考下吧
    2021-07-07
  • Redis集群设置maxmemory参数的实现

    Redis集群设置maxmemory参数的实现

    Redis集群中设置内存限制需要为每个节点单独配置maxmemory参数,本文就来介绍一下Redis集群设置maxmemory参数,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-01-01
  • 使用JMeter插件Redis Data Set如何实现高性能数据驱动测试

    使用JMeter插件Redis Data Set如何实现高性能数据驱动测试

    RedisDataSet插件是JMeter的一个插件,可以实现从Redis中动态加载数据,并将其用作测试参数,本文详细介绍如何在JMeter中使用RedisDataSet插件,帮助你实现高效的数据驱动测试
    2025-01-01
  • 完美解决linux上启动redis后配置文件未生效的问题

    完美解决linux上启动redis后配置文件未生效的问题

    今天小编就为大家分享一篇完美解决linux上启动redis后配置文件未生效的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-05-05
  • Redis中List实现双链表

    Redis中List实现双链表

    本文主要介绍了Redis中List实现双链表,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • redis lua脚本实战秒杀和减库存的实现

    redis lua脚本实战秒杀和减库存的实现

    本文主要是学习一下redis lua脚本的编写,以及在redisson这个redis客户端中是怎样使用的,实战一下秒杀场景redis减库存lua脚本的编写,并伪真实环境压测查看效果。感兴趣的可以了解一下
    2021-11-11

最新评论