使用redis做为MySQL的缓存

介绍
在实际项目中,MySQL数据库服务器有时会位于另外一台主机,需要通过网络来访问数据库;即使应用程序与MySQL数据库在同一个主机中,访问MySQL也涉及到磁盘IO操作(MySQL也有一些数据预读技术,能够减少磁盘IO读写,此部分后续继续研究),总之,直接从MySQL中读取数据不如直接从内存中读取数据来的效率高。为了提高数据库访问效率,人们采用了各种各样的方法,其中方法之一就是使用一个给予内存的缓存系统放置在数据库和应用程序之间。在查找数据的时候,首先从内存中查找,如果找到则使用,如果没有找到,那么再真正访问数据库。这种方法在一些场景下(例如:频繁查找相同数据)能够提高系统的整体效率。
本文的主要目的即介绍上文说的这样一种方法,采用redis nosql数据库作为Mysql数据库的缓存,在查找的时候,首先查找redis缓存,如果找到则返回结果;如果在redis中没有找到,那么查找Mysql数据库,找到的花则返回结果并且更新redis;如果没有找到则返回空。对于写入的情况,直接写入mysql数据库,mysql数据库通过触发器及UDF机制自动把变更的内容更新到redis中。
框图
  读取步骤:

  1. client读取redis,如果命中返回结果,如果没有命中转到2.
  2. client读取数据库,在数据库中没有查到,返回空;在数据库中查到了,返回查到的结果并更新Redis。
    写入步骤:
  3. client修改/删除或者新增数据到MySQL。
  4. MySQL的触发器调用用户自定义的UDF。
  5. 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>
  1. 安装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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/250087.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Head First Java] - 给线程命名

参考 - P503 public class RunThreads implements Runnable {public static void main (String[] args) {RunThreads runner new RunThreads();Thread alpha new Thread(runner);Thread beta new Thread(runner);alpha.setName("Alpha thread");beta.setName(&qu…

Cortex-M3 的SVC、PendSV异常,与操作系统(ucos实时系统)(转)

Cortex-M3 的SVC、PendSV异常&#xff0c;与操作系统(ucos实时系统)转载于:https://www.cnblogs.com/LittleTiger/p/10070824.html

快速排序的C++版

int Partition(int a[], int low, int high) {int x a[high];//将输入数组的最后一个数作为主元&#xff0c;用它来对数组进行划分int i low - 1;//i是最后一个小于主元的数的下标for (int j low; j < high; j)//遍历下标由low到high-1的数{if (a[j] < x)//如果数小于…

springboot---整合shiro

Shiro是一个非常不错的权限框架&#xff0c;它提供了登录和权限验证功能 1.创建数据库脚本 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0; -- ---------------------------- -- Table structure for module -- ---------------------------- DROP TABLE IF EXISTS module; C…

[Head First Java] - 线程共享数据问题

参考 - P507 1. 说明 两个线程共享同一份数据,每次使用数据时,需要先判断其是否在合理范围每次使用数据完毕使用Thread.sleep函数让线程阻塞 2.代码 class BankAccount {private int balance 100;public int getBalance() {return balance;}public void withdraw(int amou…

asp.net中提交表单数据时提示从客户端(。。。)中检测到有潜在危险的 Request.Form 值...

看到这个图是不是很亲切熟悉哈&#xff0c;做过。net的肯定都见过哈 已经 将近4年没碰。net了&#xff0c;今天正好朋友的程序有几个bug,让我帮忙修复下&#xff0c;于是我就抱着试试看的心情改了改&#xff0c;改到最后一个问题的时候也就是上面的这个问题&#xff0c;我一看&…

Openresty编写Lua代码一例

1.前段时间纠结了很久&#xff0c;一直弄不清lua和tomcat的联系。一直认为是lua调用tomcat的接口才可使用&#xff0c;后面才明白过来&#xff0c;进入了一个误区&#xff0c;lua本身就是一门独立的脚本语言。在openresty里面配置好&#xff0c;即可编写映射和响应。 下面是自己…

Shiro表结构设计

表设计 开发用户-角色-权限管理系统&#xff0c;首先我们需要知道用户-角色-权限管理系统的表结构设计。 在用户-角色-权限管理系统找那个一般会涉及5张表&#xff0c;分别为&#xff1a; 1.sys_users用户表 2.sys_roles角色表 3.sys_permissions权限表&#xff08;或资源表&…

[Java核心技术(卷I)] - 简易的日历

参考 - P102~P103 1. 目标 生成一个日历,格式如下图所示。 ps: 当前的天数需要标记为* 2. 核心 对日历的变量 import java.time.*; public class CalendarTest{public static void main(String[] args) {LocalDate date LocalDate.now(); // 获取当前日期int month date…

个人作业——福大微信公众号使用评测

案例分析&#xff1a;在福州大学公众号上&#xff0c;我们可以即时使用手机关注福大新闻&#xff0c;查看自身课表、成绩等。公众号可能存在一些小bug影响同学们的用户体验。本次作业中&#xff0c;作为一个用户——福大的学生&#xff0c;将切身体验该公众号的功能&#xff0c…

在winform中使用wpf窗体

在winform项目&#xff0c;通过引用dll可以添加WPF窗体&#xff0c;如下 但是如果直接在winform的项目中添加wpf窗体还是有部分问题&#xff0c;图片的显示。 直接在XAML界面中用Source属性设置图片会出现错误。必须通过后台代码的方式来实现。 image1.Source GetImageIcon(gl…

shiro---注解

RequiresAuthentication 验证用户是否登录&#xff0c;等同于方法subject.isAuthenticated() 结果为true时。 RequiresUser 验证用户是否被记忆&#xff0c;user有两种含义&#xff1a; 一种是成功登录的&#xff08;subject.isAuthenticated() 结果为true&#xff09;&…

[Java核心技术(卷I)] - Java中的参数能做什么和不能做什么

1. 参考 - P123 ~ P126 2. 你将学到 Java中对方法参数能做什么和不能做什么 方法不能修改基本数据类型的参数(数值型或布尔型)方法可以改变对象参数的状态方法不能让一个对象参数引用一个新的对象 3. 代码证明 public class ParamTest {public static void main(String[] ar…

软件构造 第五章第一节 可复用性的度量、形态和外部观察

第五章第一节 可复用性的度量、形态和外部观察 面向复用编程(programming for reuse)&#xff1a;开发出可复用的软件 基于复用编程(programming with reuse)&#xff1a;利用已有的可复用软件搭建应用系统 代码复用的类型&#xff1a; 白盒复用&#xff1a;源代码可见&#x…

洛谷团队月赛题:题解

10pts10pts10pts 暴力算不解释&#xff0c;时间复杂度O(knk2)O(knk^2)O(knk2)。 30pts30pts30pts 我们观察到nnn很大&#xff0c;杨辉三角会T&#xff0c;直接算会上溢&#xff0c;所以需要预处理出111~kkk逆元再算&#xff0c;时间复杂度O(knnlogkn2)O(knnlogkn^2)O(knnlogkn2…

Angular Forms - 自定义 ngModel 绑定值的方式

在 Angular 应用中&#xff0c;我们有两种方式来实现表单绑定——“模板驱动表单”与“响应式表单”。这两种方式通常能够很好的处理大部分的情况&#xff0c;但是对于一些特殊的表单控件&#xff0c;例如input[typedatetime]、input[typefile]&#xff0c;我们需要重写默认的表…

[web性能优化] - 使用在线工具对html、js、css进行压缩

参考 1. 学习点 使用 在线工具对html、css、js进行压缩学会分析压缩前后的效率提高点 2. 解决方案: 2.1 HTML压缩 在线压缩nodejs提供了 html-minifier工具(在构建层对代码进行压缩)后端模板引擎渲染压缩 2.2 CSS压缩 使用html-minifier对html中的css进行压缩使用clean-cs…

【LOJ】 #2540. 「PKUWC2018」随机算法

题解 感觉极其神奇的状压dp \(dp[i][S]\)表示答案为i&#xff0c;然后不可选的点集为S 我们每次往答案里加一个点&#xff0c;然后方案数是&#xff0c;设原来可以选的点数是y&#xff0c;新加入一个点后导致了除了新加的点之外x个点不能选&#xff0c;那么方案就是把x个数在y …

Shiro的authc过滤器的执行流程

1.先执行isAccessAllowed()&#xff0c;通过subject.isAuthenticated()判断当前session中的subject是否已经登陆过。如果在当前session即会话中已经登陆过&#xff0c;返回true&#xff0c;authc过滤器放行请求到loginUrl。 问题? 这里会有一个问题&#xff0c;如果我登陆成功…

SpringBoot之基础

简介 背景 J2EE笨重的开发 / 繁多的配置 / 低下的开发效率 / 复杂的部署流程 / 第三方技术集成难度大 特点 ① 快速创建独立运行的spring项目以及主流框架集成 ② 使用嵌入式的Servlet容器, 应用无需达成war包 ③ starters自动依赖和版本控制 ④ 大量自动配置, 简化开发, 也可修…