redis lua原理分析

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
云原生多模数据库 Lindorm,多引擎 多规格 0-4节点
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介:

redis-2.6支持通过EVAL命令来执行lua脚本,对lua脚本的支持扩展了redis的应用场景,redis支持路脚本需要做2件事

  1. redis能执行lua脚本
  2. 在lua脚本里能执行redis的命令

接下来,我将通过一个简单的实例来解析redis如何完成上述两个工作的。

构建一个简单的redis

#define DICT_SIZE 100
struct redisDict {
  char* key[DICT_SIZE];
  char* value[DICT_SIZE];
  int  idx;
};

static void setCommand(const char *key, const char *value)
{
    /* ignore memory issue for simple */
    if (dict.idx + 1 <= DICT_SIZE) {
    dict.key[dict.idx] = (char *)malloc(strlen(key) + 1);
    strcpy(dict.key[dict.idx], key);

    dict.value[dict.idx] = (char *)malloc(strlen(value) + 1);
    strcpy(dict.value[dict.idx], value);

    dict.idx += 1;
  }
}

static const char *getCommand(const char *key)
{
  int j;
  for (j = 0; j <= dict.idx; j++) {
    if (strcmp(dict.key[j], key) == 0) {
      return dict.value[j];
    }
  }
  return "KeyNotFound";
}

上述代码实现了一个伪redis,支持setCommand、getCommand。

C调用lua脚本

具体例子参考http://lua-users.org/wiki/SimpleLuaApiExample

/*
 * All Lua contexts are held in this structure. We work with it almost
 * all the time.
 */
lua_State *L = luaL_newstate();

luaL_openlibs(L); /* Load Lua libraries */

/* Load the file containing the script we are going to run */
status = luaL_loadfile(L, "script.lua");

/* Ask Lua to run our little script */
result = lua_pcall(L, 0, LUA_MULTRET, 0);

上述代码片段中,其中script.lua是一个lua脚本。redis里稍有不同,redis里的脚本是通过EVAL命令传递到服务器端,redis将脚本拼成一个lua函数,然后调用loadbuffer,而这里从文件执行脚本调用的loadfile。

lua调用C函数

下面的lua代码里,调用的是redis的setCommand和getCommand。

redis.call("set", "foo", "bar");

return redis.call("get", "foo");

要想lua脚本能调用C代码,需要现在lua环境注册对应的C函数,参考redis的scriptingInit函数。

static int call(lua_State *L)
{
  int argc = lua_gettop(L);
  const char *cmd = lua_tostring(L, 1);
  const char *key = lua_tostring(L, 2);
  if (strcmp(cmd, "set") == 0) {
    assert(argc == 3);
    const char *value = lua_tostring(L, 3);
    setCommand(key, value);
   return 0;
  } else if (strcmp(cmd, "get") == 0) {
    assert(argc == 2);
    lua_pushstring(L, getCommand(key));
    return 1;
  }

  lua_pushstring(L, "Invalid Command");
  return 1;
}

static void scriptingInit()
{

  L = luaL_newstate();

  luaL_openlibs(L);

  /* Register the redis commands table and fields */
  lua_newtable(L);

  /* redis.call */
  lua_pushstring(L, "call");
  lua_pushcfunction(L, call);
  lua_settable(L, -3);

  /* Finally set the table as 'redis' global var. */
  lua_setglobal(L, "redis");
}

完整示例代码

 1
 2
 
         
redis . call ( " set" , " foo" , " bar" );
return redis . call ( " get" , " foo" );
 来自CODE的代码片
script.lua

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 
         
/* gcc -o test_lua lua.c -llua -lm -ldl */
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

#define DICT_SIZE 100
struct redisDict {
char * key [ DICT_SIZE ];
char * value [ DICT_SIZE ];
int idx ;
};

struct redisDict dict ;
lua_State * L ;

static void setCommand ( const char * key , const char * value )
{
/* ignore memory issue for simple */
if ( dict . idx + 1 <= DICT_SIZE ) {
dict . key [ dict . idx ] = ( char * ) malloc ( strlen ( key ) + 1 );
strcpy ( dict . key [ dict . idx ], key );

dict . value [ dict . idx ] = ( char * ) malloc ( strlen ( value ) + 1 );
strcpy ( dict . value [ dict . idx ], value );

dict . idx += 1 ;
}
}

static const char * getCommand ( const char * key )
{
int j ;
for ( j = 0 ; j <= dict . idx ; j ++ ) {
if ( strcmp ( dict . key [ j ], key ) == 0 ) {
return dict . value [ j ];
}
}
return "KeyNotFound" ;
}

static int call ( lua_State * L )
{
int argc = lua_gettop ( L );
const char * cmd = lua_tostring ( L , 1 );
const char * key = lua_tostring ( L , 2 );
if ( strcmp ( cmd , "set" ) == 0 ) {
assert ( argc == 3 );
const char * value = lua_tostring ( L , 3 );
setCommand ( key , value );
return 0 ;
} else if ( strcmp ( cmd , "get" ) == 0 ) {
assert ( argc == 2 );
lua_pushstring ( L , getCommand ( key ));
return 1 ;
}

lua_pushstring ( L , "Invalid Command" );
return 1 ;
}

static void scriptingRun ( const char * filename )
{
int status = luaL_loadfile ( L , filename );
if ( status ) {
fprintf ( stderr , "Couldn't load file: %s \n " , lua_tostring ( L , - 1 ));
exit ( 1 );
}

/* Ask Lua to run our little script */
int result = lua_pcall ( L , 0 , LUA_MULTRET , 0 );
if ( result ) {
fprintf ( stderr , "Failed to run script: %s \n " , lua_tostring ( L , - 1 ));
exit ( 1 );
}

/* Get the returned value at the top of the stack (index -1) */
const char * value = lua_tostring ( L , - 1 );

printf ( "%s \n " , value );

lua_pop ( L , 1 ); /* Take the returned value out of the stack */
lua_close ( L ); /* Cya, Lua */
}

static void scriptingInit ()
{
/*
* All Lua contexts are held in this structure. We work with it almost
* all the time.
*/
L = luaL_newstate ();

/* Load Lua libraries */
luaL_openlibs ( L );

/* Register the redis commands table and fields */
lua_newtable ( L );

/* redis.call */
lua_pushstring ( L , "call" );
lua_pushcfunction ( L , call );
lua_settable ( L , - 3 );

/* Finally set the table as 'redis' global var. */
lua_setglobal ( L , "redis" );
}

int main ( void )
{
scriptingInit ();
scriptingRun ( "script.lua" );
return 0 ;
}
 来自CODE的代码片
lua.c

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
26天前
|
存储 NoSQL Redis
Redis 新版本引入多线程的利弊分析
【10月更文挑战第16天】Redis 新版本引入多线程是一个具有挑战性和机遇的改变。虽然多线程带来了一些潜在的问题和挑战,但也为 Redis 提供了进一步提升性能和扩展能力的可能性。在实际应用中,我们需要根据具体的需求和场景,综合评估多线程的利弊,谨慎地选择和使用 Redis 的新版本。同时,Redis 开发者也需要不断努力,优化和完善多线程机制,以提供更加稳定、高效和可靠的 Redis 服务。
30 1
|
18天前
|
缓存 监控 NoSQL
Redis 缓存穿透的检测方法与分析
【10月更文挑战第23天】通过以上对 Redis 缓存穿透检测方法的深入探讨,我们对如何及时发现和处理这一问题有了更全面的认识。在实际应用中,我们需要综合运用多种检测手段,并结合业务场景和实际情况进行分析,以确保能够准确、及时地检测到缓存穿透现象,并采取有效的措施加以解决。同时,要不断优化和改进检测方法,提高检测的准确性和效率,为系统的稳定运行提供有力保障。
47 5
|
1月前
|
缓存 NoSQL Java
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
57 3
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
|
23天前
|
存储 NoSQL 定位技术
Redis geo原理
Redis的GEO功能基于Earth Mapper(http://earth-api.org/)库,它允许存储地理位置信息并执行一些基于该信息的操作。
25 3
|
1月前
|
设计模式 NoSQL 网络协议
大数据-48 Redis 通信协议原理RESP 事件处理机制原理 文件事件 时间事件 Reactor多路复用
大数据-48 Redis 通信协议原理RESP 事件处理机制原理 文件事件 时间事件 Reactor多路复用
36 2
|
1月前
|
缓存 分布式计算 NoSQL
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
27 2
|
1月前
|
存储 缓存 NoSQL
大数据-46 Redis 持久化 RDB AOF 配置参数 混合模式 具体原理 触发方式 优点与缺点
大数据-46 Redis 持久化 RDB AOF 配置参数 混合模式 具体原理 触发方式 优点与缺点
56 1
|
1月前
|
NoSQL Java 关系型数据库
阿里 P7二面:Redis 执行 Lua,到底能不能保证原子性?
Redis 和 Lua,两个看似风流马不相及的技术点,为何能产生“爱”的火花,成为工作开发中的黄金搭档?技术面试中更是高频出现,Redis 执行 Lua 到底能不能保证原子性?今天就来聊一聊。 
79 1
|
1月前
|
NoSQL 关系型数据库 MySQL
Redis 事务特性、原理、具体命令操作全方位诠释 —— 零基础可学习
本文全面阐述了Redis事务的特性、原理、具体命令操作,指出Redis事务具有原子性但不保证一致性、持久性和隔离性,并解释了Redis事务的适用场景和WATCH命令的乐观锁机制。
200 0
Redis 事务特性、原理、具体命令操作全方位诠释 —— 零基础可学习
|
1月前
|
消息中间件 NoSQL Kafka
大数据-116 - Flink DataStream Sink 原理、概念、常见Sink类型 配置与使用 附带案例1:消费Kafka写到Redis
大数据-116 - Flink DataStream Sink 原理、概念、常见Sink类型 配置与使用 附带案例1:消费Kafka写到Redis
129 0

相关产品

  • 云数据库 Tair(兼容 Redis)