利用Gearman进行Mysql到Redis的复制

本文介绍了一种利用Redis作为数据库缓存的方案,并通过Gearman实现MySQL与Redis之间的数据同步,确保数据一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

redis与数据库结合,作为数据库数据的缓存提供给前端

一.实现数据库,php,nginx和redis的架构

server1提供nginx服务,使用php语言,server2提供redis缓存服务,server3提供后端数据库服务…
整体思想是:客户端通过nginx和php访问后端数据库时,先在redis这个数据库缓存中查找,看是否含有想要的数据,如果没有就去后端数据库查找,将查找到数据返回给客户端一份,另外在redis中缓存一份….下次如果访问相同的数据就直接去redis,缩短时间同时也减轻数据库的查找压力

(一)在server1前端中
1.安装nginx和php

 yum install -y nginx-1.8.0-1.el6.ngx.x86_64.rpm php-*

2.在php的配置文件中修改时区为上海

 cd /etc/php.d/  
 vim /etc/php.ini
  timezone = “Asia/Shanghai”

3.因为php作为nginx的前端服务,我们将php网页代码也限制在nginx的用户空间中

 cd /etc/php-fpm.d/ 
  vim www.conf  修改用户为nginx

这里写图片描述
4.查看端口情况
netstat -antlp
5.修改nginx的配置文件

cd /etc/nginx/conf.d   
vim default.conf   
    location / {
        root   /usr/share/nginx/html;
        index  index.php index.html index.htm;      # 设置index.php为默认发布文件


    location ~ \.php$ {
        root           html;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME /usr/share/nginx/html$fastcgi_script_name;
        include        fastcgi_params;
    }

6.将php测试页面的代码(该代码是查询数据库中数据)放在默认发布目录下

cd /usr/share/nginx/html/ 
cp test.php /usr/share/nginx/html/index.php
test.php代码如下(仅供参考):
  <?php
        $redis = new Redis();
        $redis->connect('127.0.0.1',6379) or die ("could net connect redis server");
  #      $query = "select * from test limit 9";
        $query = "select * from test";
        for ($key = 1; $key < 10; $key++)
        {
                if (!$redis->get($key))
                {
                        $connect = mysql_connect('127.0.0.1','redis','westos');  # 利用redis用户访问数据库
                        mysql_select_db(test);
                        $result = mysql_query($query);
                        #如果没有找到$key,就将该查询sql的结果缓存到redis
                        while ($row = mysql_fetch_assoc($result))
                        {
                                $redis->set($row['id'],$row['name']);
                        }
                        $myserver = 'mysql';
                        break;
                }
                else
                {
                        $myserver = "redis";
                        $data[$key] = $redis->get($key);
                }
        }
        echo $myserver;
        echo "<br>";
        for ($key = 1; $key < 10; $key++)
        {
                echo "number is <b><font color=#FF0000>$key</font></b>";

                echo "<br>";

                echo "name is <b><font color=#FF0000>$data[$key]</font></b>";

                echo "<br>";
        }
?>

7.源码编译php与redis的连接软件—phpredis-master.zip

yum install unzip      # 安装解压工具
unzip phpredis-master.zip 
cd phpredis-master   
phpize                 # 创建预编译环境
./configure     
make  
make install           # 源码编译安装

这里写图片描述
8.在php中动态加载redis模块

cd /etc/php.d   
cp mysql.ini redis.ini      # 拷贝数据库的模版
vim redis.ini
extension=redis.so          # 将redis模块加入php中

这里写图片描述
9.重新加载nginx,php和redis服务

/etc/init.d/php-fpm reload
nginx-s reload
/etc/init.d/redis stop 
/etc/init.d/redis start

10.在数据库中建数据库建表,对所有ip上的nginx用户授权(因为在php前端我们是利用nginx用户来访问数据库的)
这里写图片描述
11.在浏览器处输入提供nginx服务和php服务的主句ip,因为redis里边没有数据缓存,所以得去后端服务器拿数据,刷新页面,会看到第二次是去redis中拿数据,同时我们在提供redis的主机中利用redis-cli命令行也可以看到数据(说明数据是真的存在了redis缓存中)
数据库:
这里写图片描述
网页第二次去redis:
这里写图片描述
提供redis服务的主机:
这里写图片描述

可是做到这儿,我们可以发现问题,当在数据库中修改了数据之后,网页中的数据和 redis缓存处的数据均不改变,这样当然是不行的,所以我们解决这个问题…

二.利用Gearman进行Mysql到Redis的同步复制:
Gearman是一个开源的Map/Reduce分布式计算框架,具有丰富的client sdk,而且它支持MySQL UDF。
这里写图片描述

当有数据发生变化时,通过MySQL Trigger实时异步调用Gearman的UDF提交一个job给JobServer,当job执行的时候会去更新redis;从而保证redis与MySQL中的数据是同步的。

(一)在server1中:

1.安装Gearmand(服务端)及其很多很多的依赖项,之后打开gearmand服务

yum install  -y gearmand-1.1.8-2.el6.x86_64.rpm libgearman-* libevent-*
yum install php-devel-5.3.3-38.el6.x86_64.rpm  php-5.3.3-38.el6.x86_64.rpm

/etc/init.d/gearmand start
netstat -antlp   # 查看对应端口是否打开

2.scp gearman-mysql-udf-0.6.tar.gz server3:
scp lib_mysqludf_json-master.zip server3:
scp libgearman-* server3:
scp libevent-* server3:
3.在php中动态加入模块gearman

cp worker.php  /usr/local/   # work是中间搬运数据的“进程”,由php语言编写
ll gearman-1.1.2.tgz 
tar zxf gearman-1.1.2.tgz     # 在php中动态加入Gearman的模块,并进行源码编译
cd gearman-1.1.2  
phpize  
make make 
install


vim worker.php
<?php
$worker = new GearmanWorker();
$worker->addServer();
$worker->addFunction('syncToRedis', 'syncToRedis');

$redis = new Redis();
$redis->connect('172.25.1.2', 6379);

while($worker->work());
function syncToRedis($job)
{
        global $redis;
        $workString = $job->workload();
        $work = json_decode($workString);
        if(!isset($work->id)){
                return false;
        }
        $redis->set($work->id, $work->name);
        # 这条语句就是将 id 作 KEY 和name 作 VALUE 分开存储,需要和前面写的 php 测试代码的存取一致。
}
?>

4.将添加的动态模块加进php的模块目录中,当然也可选择直接写入配置文件中

cd /usr/lib64/php/modules/    # php的模块目录
ll gearman.so                 # 刚刚源码编译成功的gearman模块
cp redis.ini gearman.ini      # 利用redis的模版,编写gearman的模块配置文件
vim gearman.ini
  gearman.so

5.查看个gearman模块是否添加成功

/etc/init.d/php-fpm reload    # 重载服务
php -m | grep gearman         # 如果有则表明成功

6.启动worker进程并将其打入后台

nohup  php /usr/local/worker.php  &
ps ax

(二)在server3(后端数据库)中:
1.安装把mysql关系型数据转换成json格式的UDF工具—lib_mysqludf_json-master
(mysql里的数据格式转成json格式,通常使用php的json扩展实现。如果使用udf,会有更快的速度。)

unzip lib_mysqludf_json-master.zip     # 解压源码包
cd lib_mysqludf_json-master   
yum install gcc mysql-devel -y         # 安装依赖性
gcc $(mysql_config --cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c
cd /usr/lib64/mysql/plugin/            # 存放数据库服务的所有插件位置
cd /root/lib_mysqludf_json-master
cp lib_mysqludf_json.so /usr/lib64/mysql/plugin/  # 拷贝 lib_mysqludf_json.so 模块到数据库的插件目录中

这里写图片描述
2.登陆mysql,注册 json_object函数:

show global variables like 'plugin_dir';     # 查看数据库服务存放插件的目录
CREATE FUNCTION json_object RETURNS STRING SONAME 'lib_mysqludf_json.so';  # 注册 UDF 函数 json_object(作用是利用该函数可以将数据库中的数据构造为json格式)

这里写图片描述

3.安装 gearman-mysql-udf,这个插件是用来管理调用 Gearman 的分布式的队列。

tar zxf gearman-mysql-udf-0.6.tar.gz
cd gearman-mysql-udf-0.6
yum install libgearman-* libevent-*    # 解决依赖性
./configure --libdir=/usr/lib64/mysql/plugin/     # 创建预编译环境,指定安装目录在默认数据库插件目录中
make     # 源码编译
make install

这里写图片描述
4.登陆mysql,再次注册函数

CREATE FUNCTION gman_do_background RETURNS STRING SONAME 'libgearman_mysql_udf.so';     # 用Gearman做队列来实现消息推送
CREATE FUNCTION gman_servers_set RETURNS STRING SONAME 'libgearman_mysql_udf.so';
select * from mysql.func;     # 查看数据库中注册的函数
SELECT gman_servers_set('172.25.1.1:4730');     # 指定 gearman 的服务信息

这里写图片描述
这里写图片描述
5.编辑触发器文件

什么是触发器: 触发器(trigger)是SQL server
提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,比如当对一个表进行操作(insert,delete, update)时就会激活它执行。触发器经常用于加强数据的完整性约束和业务规则等

vim test.sql   
use test;       # 取数据程序段
#CREATE TABLE `test` (`id` int(7) NOT NULL AUTO_INCREMENT, `name` char(8) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#INSERT INTO `test` VALUES (1,'test1'),(2,'test2'),(3,'test3'),(4,'test4'),(5,'test5'),(6,'test6'),(7,'test7'),(8,'test8'),(9,'test9');

DELIMITER $$     # 触发器程序段
CREATE TRIGGER datatoredis AFTER UPDATE ON test FOR EACH ROW BEGIN
    SET @RECV=gman_do_background('syncToRedis', json_object(NEW.id as `id`, NEW.name as `name`));
  END$$
DELIMITER ;

将触发器程序导入数据库中:mysql < test.sql
登陆mysql:SHOW TRIGGERS FROM test; 查看触发器是否导入
这里写图片描述

测试:
在数据库中修改数据,在redis和网页中查看数据是否更新….
数据库中:
这里写图片描述
网页:
这里写图片描述
redis:
这里写图片描述
说明三处数据已经达到一致!!!

### 如何结合 RedisMySQL 实现混合或多模式的数据存储架构 #### 架构概述 Redis 是一种高性能的内存级键值存储系统,适用于快速读写的场景,而 MySQL 则是一种支持 ACID 特性的关系型数据库,适合用于持久化存储和复杂的事务处理。两者可以结合起来形成一个多模式数据存储架构,利用各自的优势来满足不同的业务需求。 这种架构的核心在于将 Redis 作为缓存层,用来加速频繁访问的数据请求,同时将 MySQL 作为后台的持久化存储,确保数据的安全性和可靠性[^1]。 --- #### 技术实现细节 ##### 数据分层设计 - **热数据存储于 Redis** 将高频访问的小量数据(如用户会话、计数器、排行榜等)存储到 Redis 中,这些数据通常具有较高的时间敏感度和较低的持久化要求[^3]。 - **冷数据存储于 MySQL** 对于需要长期保存或者涉及复杂查询的关系型数据,则将其存储在 MySQL 中。这类数据可能包括订单记录、交易历史或其他结构化的业务信息。 ##### 同步机制 为了保持 RedisMySQL 的数据一致性,可以通过以下方式实现: - **异步复制** 使用 Gearman 或其他消息队列工具完成从 MySQLRedis 的增量同步过程。当 MySQL 中的数据发生变化时,触发相应的事件通知 Redis 更新其对应的缓存条目。 - **定期刷新策略** 配置定时任务周期性地扫描 MySQL 表中的最新改动,并据此调整 Redis 缓存的内容。这种方法虽然简单易行,但在高并发环境下可能会引入额外开销[^4]。 - **双写控制逻辑** 应用程序层面负责协调两次写入操作——即先修改 MySQL 主库后再更新关联的 Redis 键值对。尽管如此,仍需注意可能出现的时间窗口竞争条件以及网络分区带来的潜在风险。 --- #### 故障恢复与高可用保障措施 考虑到分布式系统的固有特性及其运行过程中不可避免的各种异常状况,必须提前规划好应对策略以降低服务中断概率并缩短修复时间窗期长度: - **主备切换方案** 基于 Keepalived 工具配合 VRRP 协议构建虚拟 IP 地址漂移功能, 当检测到当前活动节点失效后能够迅速迁移至备用实例继续提供正常的服务响应[^2]. - **冗余副本维护** 不仅限于单一组件内部实施主从复制拓扑结构(比如 Redis Sentinel / Cluster 模式), 还应该跨不同类型的数据库间建立多层次保护屏障, 确保即使某一部分发生不可逆损坏也能依靠剩余部分重建完整的业务视图. --- ```python import redis from mysql.connector import connect def fetch_data_from_cache(key): r = redis.Redis(host='localhost', port=6379, decode_responses=True) value = r.get(key) if not value: conn = connect(user='root', password='', host='127.0.0.1', database='test') cursor = conn.cursor() query = f"SELECT * FROM users WHERE id={key}" cursor.execute(query) result = cursor.fetchone() if result: r.set(str(result[0]), str(result)) return str(result) return value or 'Not Found' ``` 上述代码片段展示了如何优先尝试从 Redis 获取指定用户的详细资料; 如果未命中则回退至 MySQL 查询原始记录并将结果重新加载进缓存以便下次更快返回相同请求的结果集. --- #### 性能优化建议 最后值得一提的是,在实际部署之前还应充分考虑以下几个方面的因素来进行必要的参数微调工作: - 调整最大连接数限制(`max_connections`)以适应预期负载水平. - 开启管道传输(pipelining)减少往返延迟影响效率表现. - 设置合理的过期策略(TTL),防止无意义占用宝贵资源空间浪费现象的发生. ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值