Redis与mysql同步之路

来源:互联网 发布:淘宝怎么查看我的评价 编辑:程序博客网 时间:2024/04/30 02:37

【java】gearman进行Mysql到Redis的复制

一.整体思路说明



以mysql数据为主,写操作(insert,update,delete)交于mysql,读操作交于redis。当数据库数据发生改变时,通过Mysql触发器(insert,update,delete)异步调用gearman的UDF提交一个job给job server(可以由PHP,也可以用java),当执行job的时候去更新redis,达到redis与mysql数据是同步的。

二、环境 mysql,redis,gearman,Centos 64位

三、简介

1.redis简介

Redis是一个开源的K-V内存数据库,它的key可以是string/set/hash/list/...,因为是基于内存的,所在访问速度相当快。

2.gearman简介

Gearman是一个开源的Map/Reduce分布式计算框架,具有丰富的client sdk,而且它支持MySQL UDF。

四、环境搭建(mysql,redis,gearman,Centos 64位)

1.redis的环境安装

linux安装redis详解

2.gearman的环境搭建

//安装依赖$ yum install -y boost-devel gperf libevent-devel libuuid-devel//下载gearman  2015-12-10来看都是最新版本$ wget https://launchpad.net/gearmand/1.2/1.1.12/+download/gearmand-1.1.12.tar.gz$ tar zxvf gearmand-1.1.12.tar.gz//编译安装,指定mysqlclient的链接路径 进入gearmand-1.1.12
$ cd gearmand-1.1.12$ ./configure$ make$ make install//启动gearmand服务端 (启动之时,在/var/log/下创建gearmand.log日志文件。-l 指定日志文件  -d后台运行 -L 0.0.0.0 绑定到IPV4)$ /usr/local/sbin/gearmand -L 0.0.0.0 -l /var/log/gearmand.log -d//查看是否启动成功[root@iZ94uyronrjZ mysql]# ps -ef | grep gearmanroot     31142     1  0 17:06 ?        00:00:00 /usr/local/sbin/gearmand -L 0.0.0.0 -l /znwx/logs/gearmand/gearmand.log -droot     31174 30507  0 17:40 pts/0    00:00:00 grep gearman//查看是否安装成功,查看gearman版本$ /usr/local/sbin/gearmand -V
安装问题解决:执行./configure(前提已安装boost)
configure:error :could not find boost
解决方法
$ yum install gcc-c++ 

3.MySQL UDF + Trigger同步数据到Gearman

3.1安装lib_mysqludf_json

//安装mysql的开发包,否则找不到mysql_config文件$ yum install -y  mysql-devel//下载lib_mysqludf_json(地址:https://github.com/mysqludf)$ cd lib_mysqludf_json先删除 lib_mysqludf_json.so//编译 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//拷贝lib_mysqludf_json.so到MySQL的plugin目录//可以登陆MySQL,输入命令"show variables like '%plugin%'"查看plugin位置$ cp lib_mysqludf_json.so /usr/lib64/mysql/plugin///演示lib_mysqludf_json功能$ mysql -uname -hhost -ppwd//首先注册UDF函数mysql> CREATE FUNCTION json_object RETURNS STRING        SONAME "lib_mysqludf_json.so";//json_array|json_members|json_values函数注册方式与json_object一样.mysql> use test;mysql> select * from user_list;+------+----------+| NAME | PASSWORD |+------+----------+| troy | pwd      |+------+----------+mysql> select json_object(name,password) as user from user_list;+----------------------------------+| user                             |+----------------------------------+| {"name":"troy","password":"pwd"} |+----------------------------------+

3.2 安装gearman-mysql-udf

//下载 2015年12月10日已知最新版本$ 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//安装libgearman-devel$ yum install libgearman-devel//编译安装//可以登陆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//登录MySQL注册UDF函数mysql> CREATE FUNCTION gman_do_background RETURNS STRING       SONAME "libgearman_mysql_udf.so";mysql> CREATE FUNCTION gman_servers_set 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地址mysql> 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.confinclude ld.so.conf.d/*.conf/usr/local/lib$ /sbin/ldconfig -v | grep gearman*

3.3 MySQL Trigger调用Gearman UDF实现同步(user_list表添加id字段)

DELIMITER $$CREATE TRIGGER user_list_data_to_redis AFTER UPDATE ON user_list  FOR EACH ROW BEGIN    SET @ret=gman_do_background('MySQLToRedis', json_object(New.id as 'id',NEW.name as 'name', NEW.password as 'password'));   END$$DELIMITER ;

说明:mysql只支持单事件的触发,insert与delete需另外配置

五、java实现job server 和worker或者单worker

说明以及问题:此类采用了gearman官网的java-gearman-service(地址:https://code.google.com/p/java-gearman-service/),目前release版本是0.6.6。CSDN下载地址
还需要的redis的ja,采用maven配置。本次演示采用本地主程序测试.
<dependency>      <groupId>redis.clients</groupId>      <artifactId>jedis</artifactId>      <version>2.1.0</version>  </dependency> 
废话不多说,直接上java代码
package com.yuanjun.webSocket.redis;import java.util.concurrent.TimeUnit;import net.sf.json.JSONObject;import org.gearman.Gearman;import org.gearman.GearmanFunction;import org.gearman.GearmanFunctionCallback;import org.gearman.GearmanServer;import org.gearman.GearmanWorker;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import com.yuanjun.webSocket.bean.Demo;/**  * mysql 同步到 redis 的工具类。连接远程gearman job server  * 实现原理   * mysql_udf  >>>>>  gearman job server>>>> gearm worker(本类) >>>>> redis  * @author yuanjun 2017年11月17日  * 说明:Config配置采用手动配置,非配置文件的方式,只作为环境搭建的实验 */  @Component  public class MysqlToRedisWorker implements GearmanFunction{      private static final Logger log = LoggerFactory.getLogger(MysqlToRedisWorker.class);      //Gearman UDF的方法名      public static final String ECHO_FUNCTION_NAME = "MySQLToRedis";      //Gearman服务的ip地址     public static final String EchoHost = "47.94.208.171";    //Gearman的默认端口    public static final int EchoPort = 4730;            /**      * 连接其他jobserver用的。      */      public void startWorker() {          Gearman gearman = Gearman.createGearman();  //创建gearman对象,无论是client,worker都是由这个对象产生的          log.info("MysqlToRedisWorkder connection:"+EchoHost+":"+EchoPort+",function:"+MysqlToRedisWorker.ECHO_FUNCTION_NAME);          GearmanServer server = gearman.createGearmanServer(EchoHost, EchoPort);  //创建gearman server,主要是server地址和端口            GearmanWorker worker = gearman.createGearmanWorker();  //正题来了,创建work节点。          worker.setReconnectPeriod(2, TimeUnit.SECONDS);  //设置超时重连时间          worker.setMaximumConcurrency(5);  //最大并发数            worker.addFunction(ECHO_FUNCTION_NAME, this);  //添加function方法          worker.addServer(server);  //将work添加到server中          log.info("MysqlToRedisWorkder is started!!!!");      }      /**     * 具体的job实现,实现redis的更新操作       */    public byte[] work(String func, byte[] data, GearmanFunctionCallback callback)              throws Exception {                    log.info("收到mysql的数据:::"+new String(data));        String jsonStr = new String(data);        //将json格式字符串转为object        JSONObject jsonobject = JSONObject.fromObject(jsonStr);        Demo user= (Demo)JSONObject.toBean(jsonobject,Demo.class);         //Jedis的连接池        JedisPool jedisPool = RedisPoolUtil.getJedisPool();        Jedis jedis  = jedisPool.getResource();        //采用hash结构插入        jedis.hset("user_"+user.getId(), "name", user.getName());        jedis.hset("user_"+user.getId(), "password", user.getPassword());        //释放资源        RedisPoolUtil.release(jedisPool, jedis);        return data;      }}   
package com.yuanjun.webSocket.bean;/** * 对于的实体类 * @author Administrator * */public class Demo {private int id ;//是否删除private boolean isdelete;private String name;private String password;public Demo(){}public Demo(int id, boolean isdelete,String name,String password){this.id = id ;this.isdelete = isdelete;this.name = name;this.password  = password;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public int getId() {return id;}public void setId(int id) {this.id = id;}public boolean isIsdelete() {return isdelete;}public void setIsdelete(boolean isdelete) {this.isdelete = isdelete;}@Overridepublic String toString() {return "Demo [id=" + id + ", isdelete=" + isdelete + ", name=" + name+ ", password=" + password + "]";}}

package com.yuanjun.webSocket.redis;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import redis.clients.jedis.JedisPoolConfig;/** * redis 连接池工具类 * @author Administrator * */public class RedisPoolUtil { //Redis服务器IP    private static String ADDR = "47.94.208.171";        //Redis的端口号    private static int PORT = 6379;        //访问密码    private static String AUTH = "123456";        //可用连接实例的最大数目,默认值为8;    //如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。    private static int MAX_ACTIVE = 1024;        //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。    private static int MAX_IDLE = 200;        //等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;    private static int MAX_WAIT = 10000;        private static int TIMEOUT = 10000;        //在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;    private static boolean TEST_ON_BORROW = true;private static volatile JedisPool jedisPool = null;private RedisPoolUtil(){}public static JedisPool getJedisPool(){if(null ==jedisPool){synchronized (JedisPool.class) {if(null == jedisPool){JedisPoolConfig poolConfig = new JedisPoolConfig();poolConfig.setMaxActive(MAX_ACTIVE);poolConfig.setMaxIdle(MAX_IDLE);poolConfig.setMaxWait(MAX_WAIT);poolConfig.setTestOnBorrow(TEST_ON_BORROW);jedisPool = new JedisPool(poolConfig, ADDR, PORT, TIMEOUT, AUTH);}}}return jedisPool;}public static void release(JedisPool jedisPool,Jedis jedis){if(null!=jedis){jedisPool.returnResource(jedis);}}}
package com.yuanjun.webSocket.Test;import com.yuanjun.webSocket.redis.MysqlToRedisWorker;/** * 演示测试 * @author Administrator * */public class Test {public static void main(String[] args) {MysqlToRedisWorker mysql = new MysqlToRedisWorker();mysql.startWorker();}}

六、测试结果展示







原创粉丝点击