MySQL到Redis数据复制方案

无论MySQL还是Redis,自身都带有数据同步的机制,像比较常用的 MySQL的Master/Slave模式 ,就是由Slave端分析Master的binlog来实现的,这样的数据复制其实还是一个异步过程,只不过当服务器都在同一内网时,异步的延迟几乎可以忽略。

那么理论上我们也可以用同样方式,分析MySQL的binlog文件并将数据插入Redis。但是这需要对binlog文件以及MySQL有非常深入的理解,同时由于 binlog存在Statement/Row/Mixedlevel多种形式 ,分析binlog实现同步的工作量是非常大的。

因此这里选择了一种开发成本更加低廉的方式,借用已经比较成熟的MySQL UDF,将MySQL数据首先放入Gearman中,然后通过一个自己编写的PHP Gearman Worker,将数据同步到Redis。比分析binlog的方式增加了不少流程,但是实现成本更低,更容易操作。

Gearman的安装与使用

Gearman 是一个支持分布式的任务分发框架。设计简洁,获得了非常广泛的支持。一个典型的Gearman应用包括以下这些部分:

  • Gearman Job Server:Gearman核心程序,需要编译安装并以守护进程形式运行在后台

  • Gearman Client:可以理解为任务的收件员,比如我要在后台执行一个发送邮件的任务,可以在程序中调用一个Gearman Client并传入邮件的信息,然后就可以将执行结果立即展示给用户,而任务本身会慢慢在后台运行。

  • Gearman Worker:任务的真正执行者,一般需要自己编写具体逻辑并通过守护进程方式运行,Gearman Worker接收到Gearman Client传递的任务内容后,会按顺序处理。

以前曾经介绍过类似的 后台任务处理项目Resque 。两者的设计其实非常接近,简单可以类比为:

  • Gearman Job Server:对应Resque的Redis部分

  • Gearman Client:对应Resque的Queue操作

  • Gearman Worker:对应Resque的Worker和Job

这里之所以选择Gearman而不是Resque是因为Gearman提供了比较好用的MySQL UDF,工作量更小。

1、安装依赖

yum install -y boost-devel gperf libevent-devel libuuid-devel

yum install mysql-devel -y

2、下载gearman

wget https://launchpad.net/gearmand/1.2/1.1.12/+download/gearmand-1.1.12.tar.gz

3、编译安装,指定mysqlclient的链接路径

tar -zxvf gearmand-1.1.12.tar.gz

cd gearmand-1.1.12

./configure

make && make install

4、启动gearmand服务端 (启动之时,在/var/log/下创建gearmand.log日志文件。-l 指定日志文件  -d后台运行 -L 0.0.0.0 绑定到IPV4

gearmand -L 0.0.0.0 -l /var/log/gearmand.log -d

5、查看是否启动成功

ps -ef | grep gearman

6、查看是否安装成功,查看gearman版本信息

gearmand -V

7、MySQL UDF + Trigger同步数据到Gearman (https://github.com/mysqludf)

安装lib_mysqludf_json(lib_mysqludf_json可以把MySQL表的数据以json数据格式输出)

wget https://github.com/mysqludf/lib_mysqludf_json/archive/master.zip

unzip master.zip

cd lib_mysqludf_json-master/

rm -rf lib_mysqludf_json.so

8、编译 mysql_config 这是mysql的配置文件,可以 find /usr -name mysql_config 搜索下在什么位置

gcc $(/usr/local/mysql/bin/mysql_config  --cflags) -shared -fPIC -o lib_mysqludf_json.so lib_mysqludf_json.c

9、拷贝lib_mysqludf_json.so到MySQL的plugin目录

(可以登陆MySQL,输入命令"show variables like '%plugin%'"查看plugin位置)

cp lib_mysqludf_json.so /usr/local/mysql/lib/plugin/

演示lib_mysqludf_json功能

登录mysql

mysql -uroot -h127.0.0.1 -p

注册UDF函数

CREATE FUNCTION json_object RETURNS STRING SONAME "lib_mysqludf_json.so";

CREATE FUNCTION json_array RETURNS STRING SONAME "lib_mysqludf_json.so";

CREATE FUNCTION json_members RETURNS STRING SONAME "lib_mysqludf_json.so";

CREATE FUNCTION json_values RETURNS STRING SONAME "lib_mysqludf_json.so";

//json_array|json_members|json_values函数注册方式与json_object一样.

select json_object(id,file_save_type,base_dir) as sys_file_save_config from sys_file_save_config;

ERROR 1123 (HY000): Can't initialize function 'json_object'; Invalid json member name - name cannot be empty

以上错误这样解决,给每个成员名称使用别名即可:

select json_object(id as id ,file_save_type as fileSaveType,app_id as appID) as sys_file_save_config from sys_file_save_config;

10、安装gearman-mysql-udf (https://launchpad.net/gearman-mysql-udf)

wget https://launchpad.net/gearman-mysql-udf/trunk/0.6/+download/gearman-mysql-udf-0.6.tar.gz

tar zxvf gearman-mysql-udf-0.6.tar.gz

cd gearman-mysql-udf-0.6

11、安装libgearman-devel

yum install libgearman-devel -y

如果没有yum源,添加epel.repo yum源

[epel]

name=Extra Packages for Enterprise Linux 6 - $basearch

#baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch

mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=$basearch

failovermethod=priority

enabled=1

gpgcheck=1

gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6

[epel-debuginfo]

name=Extra Packages for Enterprise Linux 6 - $basearch - Debug

#baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch/debug

mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-debug-6&arch=$basearch

failovermethod=priority

enabled=0

gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6

gpgcheck=1

[epel-source]

name=Extra Packages for Enterprise Linux 6 - $basearch - Source

#baseurl=http://download.fedoraproject.org/pub/epel/6/SRPMS

mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-source-6&arch=$basearch

failovermethod=priority

enabled=0

gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6

gpgcheck=1

12、编译安装

(可以登陆MySQL,输入命令"show variables like '%plugin%'"查看plugin位置, mysql_config的配置文件,以及插件库所在路径,编译之后会在此路径生成.so文件)

./configure --with-mysql=/usr/local/mysql/bin/mysql_config --libdir=/usr/local/mysql/lib/plugin/

make && make install

演示gearman-mysql-udf功能

mysql -uroot -p

CREATE FUNCTION gman_do_background RETURNS STRING SONAME "libgearman_mysql_udf.so";

CREATE FUNCTION gman_servers_set RETURNS STRING SONAME "libgearman_mysql_udf.so";

CREATE FUNCTION gman_do RETURNS STRING SONAME "libgearman_mysql_udf.so";

CREATE FUNCTION gman_do_high RETURNS STRING SONAME "libgearman_mysql_udf.so";

CREATE FUNCTION gman_do_low RETURNS STRING SONAME "libgearman_mysql_udf.so";

CREATE FUNCTION gman_do_high_background RETURNS STRING SONAME "libgearman_mysql_udf.so";

CREATE FUNCTION gman_do_low_background RETURNS STRING SONAME "libgearman_mysql_udf.so";

CREATE FUNCTION gman_sum RETURNS STRING SONAME "libgearman_mysql_udf.so";

//函数gman_do|gman_do_high|gman_do_low|gman_do_high_background|gman_do_low_background|gman_sum注册方式类似,请参考gearman-mysql-udf-0.6/README

//指定gearman job server地址

SELECT gman_servers_set('127.0.0.1:4730');

如果出现异常信息:

ERROR 1126 (HY000): Can't open shared library 'libgearman_mysql_udf.so' (errno: 11 libgearman.so.8: cannot open shared object file: No such file or directory)

表示系统找不到 libgearman.so 文件,一般so都在/usr/local/lib目录下,修改配置文件/etc/ld.so.conf,将/usr/local/lib目录加入进去即可:

$ cat /etc/ld.so.conf

include ld.so.conf.d/*.conf

/usr/local/lib

$ /sbin/ldconfig -v | grep gearman*

13、MySQL Trigger调用Gearman UDF实现同步

创建触发器

DELIMITER $$

CREATE TRIGGER test_data_to_redis AFTER UPDATE ON test FOR EACH ROW BEGIN

SET@ret=gman_do_background('syncToRedis', json_object(NEW.id AS `id`, NEW.phone AS`phone`));

END$$;

DELIMITER $$

CREATE TRIGGER test_data_to_redis2 AFTER INSERT ON test

FOR EACH ROW BEGIN

SET @ret=gman_do_background('syncToRedis2', json_object(NEW.id AS `id`, NEW.phone AS`phone`));

END$$

DELIMITER ;

DELIMITER $$

CREATE TRIGGER test_data_to_redis3 BEFORE DELETE ON test

FOR EACH ROW BEGIN

SET @ret=gman_do_background('syncToRedis3', json_object(OLD.id AS `id`, OLD.phone AS`phone`));

END$$

DELIMITER ;

说明以及问题:此类采用了gearman官网的java-gearman-service(地址:https://launchpad.net/gearman-java),目前release版本是0.6.6。java-gearman-servic.jar包中,即包括gearman server,还包括client和work客户端API。

问题:config类为spring注入的配置文件类,在worker.addFunction中,如果通过config类的属性,并且属性是从配置文件来的就会有问题。不知道为啥,写死就是OK的。此类连接远程的gearman job server。

jar包需要添加到本地jar仓库:

mvn install:install-file -Dfile=C:\software\java-gearman-service-0.6.6.jar -DgroupId=org.gearman.jgs -DartifactId=java-gearman-service -Dversion=0.6.6 -Dpackaging=jar

import java.util.concurrent.TimeUnit;

import org.gearman.Gearman;

import org.gearman.GearmanFunction;

import org.gearman.GearmanFunctionCallback;

import org.gearman.GearmanServer;

import org.gearman.GearmanWorker;

/**

* *ECHO_HOST = "192.168.125.131"为安装了Gearman并开启geramand服务的主机地址

*int ECHO_PORT = 4730默认端口为4730

*

* @author Administrator

*

*/

public class EchoWorker implements GearmanFunction {

// function name

public static final String ECHO_FUNCTION_NAME = "syncToRedis";

// job server地址

public static final String ECHO_HOST = "192.168.1.245";

// job server监听的端口

public static final int ECHO_PORT = 4730;

public static void main(String[] args) {

// 创建一个Gearman实例

Gearman gearman = Gearman.createGearman();

/*

* 创建一个jobserver

*

* Parameter 1: job server的IP地址 Parameter 2: job server监听的端口

*

* job server收到client的job,并将其分发给注册worker

*

*/

GearmanServer server = gearman.createGearmanServer(EchoWorker.ECHO_HOST, EchoWorker.ECHO_PORT);

// 创建一个Gearman的worker

GearmanWorker worker = gearman.createGearmanWorker(); // 正题来了,创建work节点。

worker.setReconnectPeriod(2, TimeUnit.SECONDS); // 设置超时重连时间

worker.setMaximumConcurrency(5); // 最大并发数

// 告诉工人如何执行工作(主要实现了GearmanFunction接口)

worker.addFunction(EchoWorker.ECHO_FUNCTION_NAME, new EchoWorker());

// worker连接服务器

worker.addServer(server);

}

@Override

public byte[] work(String function, byte[] data, GearmanFunctionCallback callback) throws Exception {

// work方法实现了GearmanFunction接口中的work方法,本实例中进行了字符串的反写

if (data != null) {

String str = new String(data);

System.out.println(str);

StringBuffer sb = new StringBuffer(str);

return sb.reverse().toString().getBytes();

} else {

return "未接收到data".getBytes();

}

}

}

import org.gearman.Gearman;

import org.gearman.GearmanClient;

import org.gearman.GearmanJobEvent;

import org.gearman.GearmanJobReturn;

import org.gearman.GearmanServer;

public class EchoClient {

public static void main(String... args) throws InterruptedException {

//创建一个Gearman实例

Gearman gearman = Gearman.createGearman();

//创建一个Gearman client

GearmanClient client = gearman.createGearmanClient();

/*

* 创建一个jobserver

*

* Parameter 1: job server的IP地址

* Parameter 2: job server监听的端口

*

*job server收到client的job,并将其分发给注册worker

*

*/

GearmanServer server = gearman.createGearmanServer(

EchoWorker.ECHO_HOST, EchoWorker.ECHO_PORT);

// 告诉客户端,提交工作时它可以连接到该服务器

client.addServer(server);

/*

* 向job server提交工作

*

* Parameter 1: gearman function名字

* Parameter 2: 传送给job server和worker的数据

*

* GearmanJobReturn返回job发热结果

*/

GearmanJobReturn jobReturn = client.submitJob(

EchoWorker.ECHO_FUNCTION_NAME, ("Hello World!").getBytes());

//遍历作业事件,直到我们打到最后文件

while (!jobReturn.isEOF()) {

//下一个作业事件

GearmanJobEvent event = jobReturn.poll();

switch (event.getEventType()) {

case GEARMAN_JOB_SUCCESS:     //job执行成功

System.out.println(new String(event.getData()));

break;

case GEARMAN_SUBMIT_FAIL:     //job提交失败

case GEARMAN_JOB_FAIL:        //job执行失败

System.err.println(event.getEventType() + ": "

+ new String(event.getData()));

default:

}

}

//关闭

gearman.shutdown();

}

}

http://gearman.org/download/

php方案:https://www.tuicool.com/articles/B7Jjaa

更多好文章分享

JAVA通过Gearman实现MySQL到Redis的数据同步(异步复制)的更多相关文章

  1. 通过Gearman实现MySQL到Redis的数据同步

    对于变化频率非常快的数据来说,如果还选择传统的静态缓存方式(Memocached.File System等)展示数据,可能在缓存的存取上会有很大的开销,并不能很好的满足需要,而Redis这样基于内存的 ...

  2. Gearman + Nodejs + MySQL UDF异步实现 MySQL 到 Redis 的数据同步

    [TOC] 1, 环境 CentOS, MySQL, Redis, Nodejs 2, Redis简介 Redis是一个开源的K-V内存数据库,它的key可以是string/set/hash/list ...

  3. TiDB 作为 MySQL Slave 实现实时数据同步

    由于 TiDB 本身兼容绝大多数的 MySQL 语法,所以对于绝大多数业务来说,最安全的切换数据库方式就是将 TiDB 作为现有数据库的从库接在主 MySQL 库的后方,这样对业务方实现完全没有侵入性 ...

  4. Redis 的主从同步(复制)

    Redis 的主从同步(复制) Redis 的主从同步(复制) 什么是主从同步(复制) 假设有两个 redis 实例 ⇒ A 和 B B 实例的内容与 A 实例的内容保持同步 那么称 A 实例是主数据 ...

  5. Redis进行数据同步

    数据库中的数据一般都涉及到需要对数据进行备份的,这样可以保证数据的安全性,并且如果将一个主设备的数据同步到多个从设备上,允许用户访问数据时可以从多个从设备进行读取, 这样还可以缓解主设备的压力,Red ...

  6. 从MySQL到Redis 提升数据迁移的效率

    场景是从MySQL中将数据导入到Redis的Hash结构中.当然,最直接的做法就是遍历MySQL数据,一条一条写入到Redis中.这样可能没什么错,但是速度会非常慢.而如果能够使MySQL的查询输出数 ...

  7. Linux mysql 5.7.23 主从复制(异步复制)

    docker容器主节点: 172.17.0.9  docker容器子节点: 172.17.0.10 异步复制: 首先确认主库和从库是否一致,最好都是刚刚初始化的干净的数据库 如果主库正在使用不能初始化 ...

  8. Mysql主从复制,实现数据同步

    大型网站为了软解大量的并发访问,除了在网站实现分布式负载均衡,远远不够.到了数据业务层.数据访问层,如果还是传统的数据结构,或者只是单单靠一台服务器扛,如此多的数据库连接操作,数据库必然会崩溃,数据丢 ...

  9. 基于 MySQL Binlog 的 Elasticsearch 数据同步实践 原

    一.背景 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品.订单等数据的多维度检索. 使用 Elasticsearch 存储业务数据可以 ...

随机推荐

  1. 解决Qt编译动态链接库could not find or load the Qt platform plugin "windows" in.问题

    最近用Qt5做了一个项目的界面,在编译成可执行文件EXE之后,运行文件,提示: This application failed to start because it could not find o ...

  2. CI脚本异常退出问题定位

    背景 在CI脚本中,使用类似如下脚本进行项目编译的计时,但在执行过程中,有时会出现CI脚本(命名为ci.sh)未完全执行的情况: #!/bin/bash -e sleep_time=$1 start_ ...

  3. 【WEB API项目实战干货系列】- WEB API入门(一)

    这篇做为这个系列的第一篇,做基本的介绍,有经验的人可以直接跳到第二部分创建 ProductController.   创建 Web API 项目 在这里我们使用VS2013, .NET 4.5.1创建 ...

  4. rewirte 规则

    Nginx Rewrite Rewirte 规则也称为规则重写,主要功能是实现浏览器访问 HTTP URL 的跳转,其正则 表达式是基于 Perl 语言.通常而言,几乎所有的 WEB 服务器均可以支持 ...

  5. Linux 基本命令-----常用操作分类

    Linux/Unix 命令格式: 命令名 [选项] [参数] 注:[]中的内容代表内容可以省略 例:$ ls $ ls -l #-l 是选项 开始符号: 文件名 或 文件夹名 .当前文件夹 ..上一级 ...

  6. mapreduce解析执行sql流程

    样例准备 编号 姓名 性别 班级编号 1 name_1 male 1 2 name_2 female 2 3 name_3 male 3 4 name_4 female 4 5 name_5 male ...

  7. fio——IO基准测试

    简介 fio是IO工具,既可以用于基准测试,也可以用于硬件的压力测试验证(stress/hardware verification). 支持13种不同的IO引擎(sync.mmap.libaio.po ...

  8. 脱掉Golang的第一层衣裳 golang入坑系列

    读前必读,博客园的文章并非最新,想看最新还是建议点击这里.博客园的文章是为了方便不能FQ的同学,同步而来的.不放在博客园,不是不支持国产,而是博客园的排版太难看了,太难看了,太难看了!而且还没有客户端 ...

  9. 手工搭建基于ABP的框架(3) - 登录,权限控制与日志

    为了防止不提供原网址的转载,特在这里加上原文链接: http://www.cnblogs.com/skabyy/p/7695258.html 本篇将实现登录.权限控制.日志配置与审计日志的功能.首先我 ...

  10. 源码怎么找之rest_framework的用户认证

    首先得有一点常识,比如用户认证,就是authenticate 比如一个函数,应该有返回值, 比如一个类里面的self,真的是代表本身这个类吗 再比如看到一个东西加括号,就两种情况,一种是函数,一种是类 ...