通过mysql自动刷新redis_通过MySQL自动同步刷新Redis

本文详细介绍了如何使用MySQL用户自定义函数(UDF)结合C++来实现与Redis的交互,从而在MySQL数据更新时自动刷新Redis缓存。首先讲解了创建UDF的基本步骤,然后展示了如何在UDF中调用Redis API进行数据操作,最后通过MySQL触发器监听数据变化,实现实时同步到Redis。整个过程旨在减少数据不一致,提高系统效率。
摘要由CSDN通过智能技术生成

1 /*simple.cpp*/

2 #include

3

4 extern "C" long long simple_add(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)

5 {

6 int a = *((long long *)args->args[0]);

7 int b = *((long long *)args->args[1]);

8 return a + b;

9 }

10

11 extern "C" my_bool simple_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)

12 {

13 return 0;

14 }

33f0db81972a4a2164468834f626470b.gif

由于mysql提供的接口是C实现的,我们在C++中使用时需要添加:

extern "C" { ... }

接下来编译成动态库.so:

[zhxilin@localhost mysql-redis-test]$ g++ -shared -fPIC -I /usr/include/mysql -o simple_add.so simple.cpp

-shared 表示编译和链接时使用的是全局共享的类库;

-fPIC编译器输出位置无关的目标代码,适用于动态库;

-I /usr/include/mysql 指明包含的头文件mysql.h所在的位置。

编译出simple_add.so后用root拷贝到/usr/lib64/mysql/plugin下:

[root@localhost mysql-redis-test]# cp simple_add.so /usr/lib64/mysql/plugin/

紧接着可以在MySQL中创建函数执行了。登录MySQL,创建关联函数:

mysql> CREATE FUNCTION simple_add RETURNS INTEGER SONAME ‘simple_add.so‘;

Query OK, 0 rows affected (0.04 sec)

测试UDF函数:

33f0db81972a4a2164468834f626470b.gif

mysql> select simple_add(10, 5);

+-------------------+

| simple_add(10, 5) |

+-------------------+

| 15 |

+-------------------+

1 row in set (0.00 sec)

33f0db81972a4a2164468834f626470b.gif

可以看到,UDF正确执行了加法。

创建UDF函数的语法是 CREATE FUNCTION xxx RETURNS [INTEGER/STRING/REAL] SONAME ‘[so name]‘;

删除UDF函数的语法是 DROP FUNCTION simple_add;

mysql> DROP FUNCTION simple_add;

Query OK, 0 rows affected (0.03 sec)

三. 在UDF中访问Redis

跟上述做法一样,只需在UDF里调用Redis提供的接口函数。Redis官方给出了Redis C++ Client (https://github.com/mrpi/redis-cplusplus-client),封装了Redis的基本操作。

源码是依赖boost,需要先安装boost:

[root@localhost dev]# yum install boost boost-devel

然后下载redis cpp client源码:

[root@localhost dev]# git clone https://github.com/mrpi/redis-cplusplus-client

使用时需要把redisclient.h、anet.h、fmacros.h、anet.c 这4个文件考到目录下,开始编写关于Redis的UDF。我们定义了redis_hset作为主函数,连接Redis并调用hset插入哈希表,redis_hset_init作为初始化,检查参数个数和类型。

33f0db81972a4a2164468834f626470b.gif

1 /* test.cpp */

2 #include

3 #include

4 #include "redisclient.h"

5 using namespace boost;

6 using namespace std;

7

8 static redis::client *m_client = NULL;

9

10 extern "C" char *redis_hset(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error) {

11 try {

12 // 连接Redis

13 if(NULL == m_client) {

14 const char* c_host = getenv("REDIS_HOST");

15 string host = "127.0.0.1";

16 if(c_host) {

17 host = c_host;

18 }

19 m_client = new redis::client(host);

20 }

21

22 if(!(args->args && args->args[0] && args->args[1] && args->args[2])) {

23 *is_null = 1;

24 return result;

25 }

26

27 // 调用hset插入一个哈希表

28 if(m_client->hset(args->args[0], args->args[1], args->args[2])) {

29 return result;

30 } else {

31 *error = 1;

32 return result;

33 }

34 } catch (const redis::redis_error& e) {

35 return result;

36 }

37 }

38

39 extern "C" my_bool redis_hset_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {

40 if (3 != args->arg_count) {

41 // hset(key, field, value) 需要三个参数

42 strncpy(message, "Please input 3 args for: hset(‘key‘, ‘field‘, ‘value‘);", MYSQL_ERRMSG_SIZE);

43 return -1;

44 }

45 if (args->arg_type[0] != STRING_RESULT ||

46 args->arg_type[1] != STRING_RESULT ||

47 args->arg_type[2] != STRING_RESULT) {

48 // 检查参数类型

49 strncpy(message, "Args type error: hset(‘key‘, ‘field‘, ‘value‘);", MYSQL_ERRMSG_SIZE);

50 return -1;

51 }

52

53 args->arg_type[0] = STRING_RESULT;

54 args->arg_type[1] = STRING_RESULT;

55 args->arg_type[2] = STRING_RESULT;

56

57 initid->ptr = NULL;

58 return 0;

59 }

33f0db81972a4a2164468834f626470b.gif

编译链接:

[zhxilin@localhost mysql-redis-test]$ g++ -shared -fPIC -I /usr/include/mysql -lboost_serialization -lboost_system -lboost_thread -o libmyredis.so anet.c test.cpp

编译时需要加上-lboost_serialization -lboost_system -lboost_thread, 表示需要链接三个动态库:libboost_serialization.so、libboost_system.so、libboost_thread.so,否则在运行时会报缺少函数定义的错误。

编译出libmyredis.so之后,将其拷贝到mysql的插件目录下并提权:

[root@localhost mysql-redis-test]# cp libmyredis.so /usr/lib64/mysql/plugin/ & chmod 777 /usr/lib64/mysql/plugin/libmyredis.so

完成之后登录MySQL,创建关联函数测试一下:

mysql> DROP FUNCTION IF EXISTS `redis_hset`;

Query OK, 0 rows affected (0.16 sec)

mysql> CREATE FUNCTION redis_hset RETURNS STRING SONAME ‘libmyredis.so‘;

Query OK, 0 rows affected (0.02 sec)

先删除老的UDF,注意函数名加反引号(``)。调用UDF测试,返回0,执行成功:

33f0db81972a4a2164468834f626470b.gif

mysql> SELECT redis_hset(‘zhxilin‘, ‘id‘, ‘09388334‘);

+-----------------------------------------+

| redis_hset(‘zhxilin‘, ‘id‘, ‘09388334‘) |

+-----------------------------------------+

| 0 |

+-----------------------------------------+

1 row in set (0.00 sec)

33f0db81972a4a2164468834f626470b.gif

打开redis-cli,查看结果:

127.0.0.1:6379> HGETALL zhxilin

1) "id"

2) "09388334"

四. 通过MySQL触发器刷新Redis

在上一节的基础上,我们想让MySQL在增删改查的时候自动调用UDF,还需要借助MySQL触发器。触发器可以监听INSERT、UPDATE、DELETE等基本操作。在MySQL中,创建触发器的基本语法如下:

CREATE TRIGGER trigger_name

trigger_time

trigger_event ON table_name

FOR EACH ROW

trigger_statement

trigger_time表示触发时机,值为AFTER或BEFORE;

trigger_event表示触发的事件,值为INSERT、UPDATE、DELETE等;

trigger_statement表示触发器的程序体,可以是一句SQL语句或者调用UDF。

在trigger_statement中,如果有多条SQL语句,需要用BEGIN...END包含起来:

BEGIN

[statement_list]

END

由于MySQL默认的结束分隔符是分号(;),如果我们在BEGIN...END中出现了分号,将被标记成结束,此时没法完成触发器的定义。有一个办法,可以调用DELIMITER命令来暂时修改结束分隔符,用完再改会分号即可。比如改成$:

mysql> DELIMITER $

我们开始定义一个触发器,监听对Student表的插入操作,Student表在上一篇文章中创建的,可以查看上一篇文章。

33f0db81972a4a2164468834f626470b.gif

mysql > DELIMITER $

> CREATE TRIGGER tg_student

> AFTER INSERT on Student

> FOR EACH ROW

> BEGIN

> SET @id = (SELECT redis_hset(CONCAT(‘stu_‘, new.Sid), ‘id‘, CAST(new.Sid AS CHAR(8))));

> SET @name = (SELECT redis_hset(CONCAT(‘stu_‘, new.Sid), ‘name‘, CAST(new.Sname AS CHAR(20))));

> Set @age = (SELECT redis_hset(CONCAT(‘stu_‘, new.Sid), ‘age‘, CAST(new.Sage AS CHAR)));

> Set @gender = (SELECT redis_hset(CONCAT(‘stu_‘, new.Sid), ‘gender‘, CAST(new.Sgen AS CHAR)));

> Set @dept = (SELECT redis_hset(CONCAT(‘stu_‘, new.Sid), ‘department‘, CAST(new.Sdept AS CHAR(10))));

> END $

33f0db81972a4a2164468834f626470b.gif

创建完触发器可以通过show查看,或者drop删除:

mysql> SHOW TRIGGERS;

mysql> DROP TRIGGER tg_student;

接下来我们调用一句插入语句,然后观察Redis和MySQL数据的变化:

mysql> INSERT INTO Student VALUES(‘09388165‘, ‘Rose‘, 19, ‘F‘, ‘SS3-205‘);

Query OK, 1 row affected (0.27 sec)

MySQL的结果:

33f0db81972a4a2164468834f626470b.gif

mysql> SELECT * FROM Student;

+----------+---------+------+------+---------+

| Sid | Sname | Sage | Sgen | Sdept |

+----------+---------+------+------+---------+

| 09388123 | Lucy | 18 | F | AS2-123 |

| 09388165 | Rose | 19 | F | SS3-205 |

| 09388308 | zhsuiy | 19 | F | MD8-208 |

| 09388318 | daemon | 18 | M | ZS4-630 |

| 09388321 | David | 20 | M | ZS4-731 |

| 09388334 | zhxilin | 20 | M | ZS4-722 |

+----------+---------+------+------+---------+

6 rows in set (0.00 sec)

33f0db81972a4a2164468834f626470b.gif

Redis的结果:

33f0db81972a4a2164468834f626470b.gif

127.0.0.1:6379> HGETALL stu_09388165

1) "id"

2) "09388165"

3) "name"

4) "Rose"

5) "age"

6) "19"

7) "gender"

8) "F"

9) "department"

10) "SS3-205"

33f0db81972a4a2164468834f626470b.gif

以上结果表明,当MySQL插入数据时,通过触发器调用UDF,实现了自动刷新Redis的数据。另外,调用MySQL插入的命令,可以通过C++实现,进而就实现了在C++的业务逻辑里,只需调用MySQL++的接口就能实现MySQL数据库和Redis缓存的更新,这部分内容在上一篇文章已经介绍过了。

总结

通过实践,能体会到MySQL和Redis是多么相亲相爱吧!^_^

本篇文章讲了从最基础的UDF开始,再到通过UDF连接Redis插入数据,再进一步介绍通过MySQL Trigger自动更新Redis数据的整个思路,实现了一个目标,即只在业务代码中更新MySQL数据库,进而Redis能够自动同步刷新。

MySQL对UDF函数和触发器的支持,使得实现Redis数据和MySQL自动同步成了可能。当然UDF毕竟是通过插件的形式运行在MySQL中的,并没有过多的安全干预,一旦插件发生致命性崩溃,有可能MySQL也会挂,所以在编写UDF的时候需要非常谨慎!

通过MySQL自动同步刷新Redis

标签:基本操作   安全   blank   瓶颈   bool   最简   cli   *args   system

本条技术文章来源于互联网,如果无意侵犯您的权益请点击此处反馈版权投诉

本文系统来源:http://www.cnblogs.com/fire909090/p/6769905.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值