介紹
在實際項目中,MySQL數據庫服務器有時會位于另外一臺主機,需要通過網絡來訪問數據庫;即使應用程序與MySQL數據庫在同一個主機中,訪問MySQL也涉及到磁盤IO操作(MySQL也有一些數據預讀技術,能夠減少磁盤IO讀寫,此部分后續繼續研究),總之,直接從MySQL中讀取數據不如直接從內存中讀取數據來的效率高。為了提高數據庫訪問效率,人們采用了各種各樣的方法,其中方法之一就是使用一個給予內存的緩存系統放置在數據庫和應用程序之間。在查找數據的時候,首先從內存中查找,如果找到則使用,如果沒有找到,那么再真正訪問數據庫。這種方法在一些場景下(例如:頻繁查找相同數據)能夠提高系統的整體效率。
本文的主要目的即介紹上文說的這樣一種方法,采用redis nosql數據庫作為Mysql數據庫的緩存,在查找的時候,首先查找redis緩存,如果找到則返回結果;如果在redis中沒有找到,那么查找Mysql數據庫,找到的花則返回結果并且更新redis;如果沒有找到則返回空。對于寫入的情況,直接寫入mysql數據庫,mysql數據庫通過觸發器及UDF機制自動把變更的內容更新到redis中。
框圖
讀取步驟:
- client讀取redis,如果命中返回結果,如果沒有命中轉到2.
- client讀取數據庫,在數據庫中沒有查到,返回空;在數據庫中查到了,返回查到的結果并更新Redis。
寫入步驟: - client修改/刪除或者新增數據到MySQL。
- MySQL的觸發器調用用戶自定義的UDF。
- UDF把修改/刪除或者新增的數據更新到redis中。
代碼實現
軟件需求
redis server與client安裝,redis編程相關的c庫。
mysql server安裝,mysql-devel包的安裝,此包包含操作mysql數據庫的C語言API包。
實現步驟
6. 安裝并驗證redis
127.0.0.1:6379> hgetall w3ckey
(empty list or set) #最開始在reids中沒有w3ckey的K-V對。
127.0.0.1:6379>
- 安裝MySQL數據庫服務器
2.1 創建MySQL數據庫的腳本如下
drop database if exists mysqlRedis;
create database mysqlRedis;
use mysqlRedis;create table test1(id INT NOT NULL AUTO_INCREMENT,name VARCHAR(64),age INT,description VARCHAR(1000),primary key(id));
2.2 創建UDF使用的動態庫
#include <stdio.h>
#include <stdlib.h>
#include <mysql.h>
#include <string.h>
#include <hiredis/hiredis.h>int gxupdate(UDF_INIT * initid, UDF_ARGS * args, char * is_null, char * error) {redisContext * c = redisConnect("127.0.0.1", 6379);if(c->err) {redisFree(c);return 1;}/*//如果設有密碼為ubunturedisReply *reply;char strReply[] = "AUTH ubuntu";reply = (redisReply*)redisCommand(c, strReply);freeReplyObject(reply);reply = NULL;*/const char * command1 = "HMSET w3ckey id %d name %s age %d description %s";redisReply * r = (redisReply *)redisCommand(c, command1,*(int*)args->args[0], args->args[1], *(int *)args->args[2], args->args[3]);if (r == NULL) {return 1;}if (!((r->type == REDIS_REPLY_STATUS) && (strcasecmp(r->str, "OK") == 0))) {freeReplyObject(r);redisFree(c);return 1;}freeReplyObject(r);return 0;
}my_bool gxupdate_init(UDF_INIT * initid, UDF_ARGS * args, char * message) {return 0;
}
編譯為動態庫:
gcc -shared -fPIC -I /usr/include/mysql -o udfredis.so mysqlUDFdemo.c /usr/local/lib/libhiredis.a
編譯完成之后拷貝動態庫udfgx.so到 /usr/lib/mysql/plugin/文件夾中,并修改成用戶對應權限。
2.3 配置udf與trigger。
use mysqlRedis;drop function if exists gxupdate;
create function gxupdate returns INTEGER soname "udfredis.so";drop trigger if exists insert_redis;
drop trigger if exists update_redis;
drop trigger if exists delete_redis;delimiter |create trigger insert_redisafter insert on test1for each rowbegindeclare ret int;select gxupdate(NEW.id, NEW.name, NEW.age, NEW.description) into @ret;
#必須加into @ret,否則返回錯誤ERROR 1415 (0A000)
#at line 6: Not allowed to return a result set from a trigger
#insert只有NEW變量。
#update有NEW和OLD變量。
#delete只有OLD變量。
end|create trigger update_redisafter update on test1for each rowbegindeclare ret int;select gxupdate(NEW.id, NEW.name, NEW.age, NEW.description) into @ret;end|create trigger delete_redisafter delete on test1for each rowbegindeclare ret int;select gxupdate(OLD.id, OLD.name, OLD.age, OLD.description) into @ret;end|delimiter ;
注意,在MySQL中創建UDF的時候,insert, update和delete不能寫成一個觸發器,只能分別定義成三個觸發器。
測試
查看redis
[root@VM_24_16_centos mysql_redis]# redis-cli
127.0.0.1:6379> hgetall w3ckey
(empty list or set)
127.0.0.1:6379>
redis中無key w3ckey 對應的value。
insert MySQL
mysql> insert into test1 (name, age, description) values ("ggglwlop", 23, "ddddgdg");
Query OK, 1 row affected (0.02 sec)mysql>
插入mysql。
查看redis
127.0.0.1:6379> hgetall w3ckey
1) "name"
2) "ggglwlop"
3) "description"
4) "ddddgdg"
5) "likes"
6) "27"
7) "visitors"
8) "23"
127.0.0.1:6379>
MySQL中有了對應的數據,說明mysql通過triger+udf的方式把改動更新到了redis中。
有用的鏈接
http://blog.csdn.net/socho/article/details/52292064
https://www.cnblogs.com/linuxbug/p/4950626.html
https://www.cnblogs.com/tommy-huang/p/4703514.html 使用redis作為mysql緩存時的redis結構設計。
http://blog.csdn.net/shikaiwencn/article/details/51792059 需要根據實際需求來靈活設計redis kv關系。
https://www.cnblogs.com/bruceleeliya/archive/2009/05/23/Linux-C-Mysql.html 使用mysql的C API訪問mysql。
https://www.2cto.com/database/201110/108925.html #mysql udf。
https://www.cnblogs.com/linuxbug/p/4950626.html #udf使用的一個例子。
https://www.jianshu.com/p/4381a38403a1
http://blog.csdn.net/socho/article/details/52292064