├── src ├── main │ ├── .DS_Store │ ├── resources │ │ ├── binlog2Hive_conf.properties │ │ ├── mysql.properties │ │ ├── core-site.xml │ │ ├── hdfs-site.xml │ │ └── log4j.properties │ └── java │ │ └── com │ │ ├── config │ │ ├── DatabaseConnection.java │ │ └── Config.java │ │ ├── common │ │ ├── ExecCMD.java │ │ ├── BinlogOption.java │ │ └── FSUtils.java │ │ ├── main │ │ └── Binlog2Hive.java │ │ └── bixuange │ │ └── BinlogClient.java └── test │ └── java │ └── com │ └── bixuange │ ├── OrcTest.java │ ├── FileSystemTest.java │ ├── BinlogClientTest.java │ └── AppTest.java ├── doc └── CREATE_TABLE.sql ├── README.md ├── pom.xml └── Binlog2Hive.iml /src/main/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MOBIN-F/Binlog2Hive/HEAD/src/main/.DS_Store -------------------------------------------------------------------------------- /src/main/resources/binlog2Hive_conf.properties: -------------------------------------------------------------------------------- 1 | ##hive表的路径 2 | default_table1_path = /DATA/PUBLIC/TABLE1 -------------------------------------------------------------------------------- /doc/CREATE_TABLE.sql: -------------------------------------------------------------------------------- 1 | ##记录Binlog同步游标的表 2 | CREATE TABLE binglogDB.t_position(nextposition LONG ,binlogfilename VARCHAR(20)) 3 | 4 | CREATE EXTERNAL TABLE IF NOT EXISTS TABLE1( 5 | KEYID bigint comment '记录序号', 6 | DDATETIME string comment '数据时间', 7 | DRP double comment 'xxx', 8 | DHP double comment 'xxx', 9 | DVP double comment 'xxx', 10 | YYP double comment 'xxx', 11 | )partitioned by (day int) 12 | ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' 13 | LINES TERMINATED BY '\n' 14 | location '/DATA/PUBLIC/TABLE1/'; -------------------------------------------------------------------------------- /src/main/resources/mysql.properties: -------------------------------------------------------------------------------- 1 | driverClassName = com.mysql.jdbc.Driver 2 | url = jdbc:mysql://localhost:3306/binlogdb?characterEncoding=utf-8&rewriteBatchedStatements=true 3 | username = root 4 | password = root 5 | 6 | initialSize = 5 7 | maxActive = 10 8 | minIdle = 3 9 | maxWait = 600000 10 | removeAbandoned = true 11 | removeAbandonedTimeout = 180 12 | timeBetweenEvictionRunsMillis = 60000 13 | minEvictableIdleTimeMillis = 300000 14 | validationQuery = SELECT 1 FROM DUAL 15 | testWhileIdle = true 16 | testOnBorrow = false 17 | testOnReturn = false 18 | poolPreparedStatements = false 19 | maxPoolPreparedStatementPerConnectionSize = 50 20 | filters = stat -------------------------------------------------------------------------------- /src/main/resources/core-site.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | fs.defaultFS 6 | hdfs://localhost:9001 7 | 8 | 9 | io.compression.codecs 10 | org.apache.hadoop.io.compress.DefaultCodec,org.apache.hadoop.io.compress.GzipCodec,org.apache.hadoop.io.compress.BZip2Codec,org.apache.hadoop.io.compress.DeflateCodec,org.apache.hadoop.io.compress.SnappyCodec,org.apache.hadoop.io.compress.Lz4Codec,com.hadoop.compression.lzo.LzoCodec,com.hadoop.compression.lzo.LzopCodec 11 | 12 | 13 | io.compression.codec.lzo.class 14 | com.hadoop.compression.lzo.LzoCodec 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/com/config/DatabaseConnection.java: -------------------------------------------------------------------------------- 1 | package com.config; 2 | import com.alibaba.druid.pool.DruidDataSourceFactory; 3 | 4 | import javax.sql.DataSource; 5 | import java.io.InputStream; 6 | import java.sql.Connection; 7 | import java.sql.SQLException; 8 | import java.util.Properties; 9 | 10 | /** 11 | * Created by Mobin on 2017/9/12. 12 | */ 13 | public class DatabaseConnection { 14 | private static DataSource MysqlDataSource; 15 | public DatabaseConnection(){} 16 | static { 17 | try{ 18 | InputStream in = DatabaseConnection.class.getClassLoader() 19 | .getResourceAsStream("mysql.properties"); 20 | Properties props = new Properties(); 21 | props.load(in); 22 | MysqlDataSource = DruidDataSourceFactory.createDataSource(props); 23 | }catch(Exception ex){ 24 | ex.printStackTrace(); 25 | } 26 | } 27 | public static Connection getMysqlConnection() throws SQLException{ 28 | return MysqlDataSource.getConnection(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/resources/hdfs-site.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dfs.client.block.write.replace-datanode-on-failure.enable 6 | true 7 | 8 | 9 | dfs.client.block.write.replace-datanode-on-failure.policy 10 | NEVER 11 | 12 | 13 | dfs.replication 14 | 1 15 | 16 | 17 | dfs.datanode.fsdataset.volume.choosing.policy 18 | org.apache.hadoop.hdfs.server.datanode.fsdataset.AvailableSpaceVolumeChoosingPolicy 19 | 20 | 21 | dfs.datanode.available-space-volume-choosing-policy.balanced-space-threshold 22 | 10737418240 23 | 24 | 25 | dfs.datanode.available-space-volume-choosing-policy.balanced-space-preference-fraction 26 | 0.75 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/com/config/Config.java: -------------------------------------------------------------------------------- 1 | package com.config; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.Properties; 6 | 7 | /** 8 | * Created with IDEA 9 | * Creater: MOBIN 10 | * Date: 2018/5/17 11 | * Time: 上午10:20 12 | */ 13 | public class Config { 14 | 15 | private static final Properties config = loadConfig(); 16 | 17 | private static Properties loadConfig(){ 18 | InputStream in = Config.class.getResourceAsStream("/binlog2Hive_conf.properties"); 19 | Properties config = new Properties(); 20 | try { 21 | config.load(in); 22 | in.close(); 23 | } catch (IOException e) { 24 | throw new RuntimeException("Failed to load binlog2Hive_conf.properties"); 25 | } 26 | return config; 27 | } 28 | 29 | public static String getStringProperty(String key){ 30 | String value = config.getProperty(key); 31 | if (value == null) 32 | return null; 33 | return value.trim(); 34 | } 35 | 36 | public static String getPath(String tableName){ 37 | String path = getStringProperty("default_" + tableName + "_path"); 38 | return path; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 项目背景 2 | >RDS的数据实时同步到HDFS下,并映射到Hive 3 | 4 | ## 原理 5 | >通过解析RDS的binlog将RDS的增量数据同步到HDFS下,并映射加载到Hive外部分区表 6 | 7 | 由于RDS表中的第二个字段都为datetime字段,所以刚才以该字段作为Hive的分区字段 8 | 9 | ### 配置文件介绍 10 | * doc/creat table.sql:Hive表的建表语句,除了静态表外,其他全部为天级别外部分区表 11 | * binglog2Hive_conf.properties:里面为所有全部需要同步到HDFS的表 12 | * mysql.properties:Mysql druid连接池配置 13 | 14 | ### 程序说明 15 | binlog解析框架:https://github.com/shyiko/mysql-binlog-connector-java 16 | 17 | 核心类为BinlogClient 18 | 1. 程序主要序列化以下几个事件 19 | * TABLE_MAP:包括表名,数据库名 20 | * WRITE_ROWS:包含增量的业务记录 21 | 22 | 2. 23 | * 程序启动时会先从t_position表中获取上次的同步状态,根据上次的同步状态来确定binlog的读取位置 24 | * BinaryLogClient首先对TABLE_MAP事件进行序列化,再结合binlog2Hive_conf.propertiesd配置过滤出我们需要同步的表再对WRITE_ROW事件进行序列化 25 | * 解析WRITE_ROWS时,将存储到ConcurrentHashMap> mapA中 26 | * 解析的记录超过一定阀值option.countInterval后再统一写HDFS文件 27 | * 写HDFS文件时,遍历mapA,根据表名分类,整理成存储到 28 | ConcurrentHashMap> mapB,最后再统一遍历mapB将数据写入到HDFS,写到哪个文件中是根据mapB的key来确定的 29 | * 文件操作类在FSUtils中,写文件时以下三种情况 30 | 1. 如果目录不存在就创建文件并将Hive表的分区映射到这个路径下, 31 | 2. 文件已存在且文件大小小于250MB就以追加的方式写文件 32 | 3. 文件大小超250MB就重新写成另一个新文件,以HDFS_BLOCK_SIZE为标准 33 | * 文件写入成功后将当前的同步状态(binlogfilename,nextposition)更新到t_position表中 34 | 35 | **项目已经去掉敏感业务信息** 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/main/java/com/common/ExecCMD.java: -------------------------------------------------------------------------------- 1 | package com.common; 2 | 3 | import com.google.common.io.LineReader; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStreamReader; 9 | 10 | /** 11 | * Created with IDEA 12 | * Creater: MOBIN 13 | * Date: 2018/5/18 14 | * Time: 下午9:56 15 | */ 16 | public class ExecCMD { 17 | private static final Logger log = LoggerFactory.getLogger(ExecCMD.class); 18 | 19 | public static String exec(String cmd){ 20 | try { 21 | String[] cmds = {"/bin/sh", "-c", cmd}; 22 | Process process = Runtime.getRuntime().exec(cmds); 23 | LineReader lineReader = new LineReader(new InputStreamReader(process.getInputStream())); 24 | StringBuffer sb = new StringBuffer(); 25 | String line; 26 | while ((line = lineReader.readLine()) != null){ 27 | sb.append(line).append("\n"); 28 | } 29 | String rs = sb.toString(); 30 | if (!cmd.isEmpty()) 31 | log.info("cmd executed, result: " + rs); 32 | return rs; 33 | } catch (IOException e) { 34 | log.error("Failed to exec cmd: " + cmd, e); 35 | } 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/main/Binlog2Hive.java: -------------------------------------------------------------------------------- 1 | package com.main; 2 | 3 | import com.common.BinlogOption; 4 | import com.bixuange.BinlogClient; 5 | import com.common.FSUtils; 6 | import org.apache.hadoop.fs.FileSystem; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.io.IOException; 11 | 12 | /** 13 | * Created with IDEA 14 | * Creater: MOBIN 15 | * Date: 2018/5/20 16 | * Time: 下午10:09 17 | */ 18 | public class Binlog2Hive { 19 | private static final Logger log = LoggerFactory.getLogger(Binlog2Hive.class); 20 | static { 21 | //-DHADOOP_USER_NAME=hdfs 22 | System.setProperty("HADOOP_USER_NAME","hdfs"); 23 | } 24 | //227527999,binlogFileName文件为mysql-bin.000043 25 | //Users/mobin/Downloads/RDS-binlog/mysql-bin.000642 26 | public static void main(String[] args) throws Exception { 27 | BinlogClient client = new BinlogClient(); 28 | BinlogOption option = new BinlogOption(args); 29 | FileSystem fs = FSUtils.getFileSystem(); 30 | registerCloseFSHook(fs); 31 | try { 32 | if (option.syncByBinlogFile) { 33 | client.readDataFromBinlogFile(option.binlogFilePath, fs, option); 34 | } else { 35 | client.replicationStream(fs, option); 36 | } 37 | } catch (Exception e) { 38 | e.printStackTrace(); 39 | } 40 | 41 | } 42 | 43 | 44 | public static void registerCloseFSHook(FileSystem fs) { 45 | log.info("注册FileSystem钩子"); 46 | //只有JVM退出时才关闭fs 47 | Runtime.getRuntime().addShutdownHook(new Thread() { 48 | @Override 49 | public void run() { 50 | try { 51 | log.info("关闭FS"); 52 | fs.close(); 53 | } catch (IOException e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/com/bixuange/OrcTest.java: -------------------------------------------------------------------------------- 1 | //package com.bixuange; 2 | // 3 | //import org.apache.hadoop.conf.Configuration; 4 | //import org.apache.hadoop.fs.Path; 5 | //import org.apache.hadoop.hive.ql.exec.vector.BytesColumnVector; 6 | //import org.apache.hadoop.hive.ql.exec.vector.LongColumnVector; 7 | //import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; 8 | //import org.apache.orc.OrcFile; 9 | //import org.apache.orc.Writer; 10 | //import org.junit.Test; 11 | //import org.apache.orc.TypeDescription; 12 | // 13 | //import java.io.IOException; 14 | //import java.nio.charset.Charset; 15 | // 16 | // 17 | ///** 18 | // * Created with IDEA 19 | // * Creater: MOBIN 20 | // * Date: 2018/5/25 21 | // * Time: 上午10:11 22 | // */ 23 | //public class OrcTest { 24 | // @Test 25 | // public void orcTest() throws IOException { 26 | // Configuration conf = new Configuration(); 27 | // Path path = new Path("/mobin1.orc"); 28 | // //typeName不能有空格 29 | // TypeDescription schema = TypeDescription.fromString("struct"); 30 | // Writer writer = OrcFile.createWriter(path, OrcFile.writerOptions(conf).setSchema(schema)); 31 | // VectorizedRowBatch batch = schema.createRowBatch(); 32 | // LongColumnVector id = (LongColumnVector) batch.cols[0]; 33 | // BytesColumnVector name = (BytesColumnVector) batch.cols[1]; 34 | // final int BATCH_SIZE = batch.getMaxSize(); 35 | // System.out.println(BATCH_SIZE); 36 | // for (int i = 0; i < 10; i ++){ 37 | // int row = batch.size ++; 38 | // id.vector[row] = i; 39 | // byte[] buffer = ("name" + i).getBytes(Charset.forName("UTF-8")); 40 | // name.setRef(row, buffer, 0, buffer.length); 41 | // if (row == BATCH_SIZE - 1){ 42 | // writer.addRowBatch(batch); 43 | // batch.reset(); 44 | // } 45 | // } 46 | // if (batch.size != 0){ 47 | // writer.addRowBatch(batch); 48 | // batch.reset(); 49 | // } 50 | // writer.close(); 51 | // } 52 | //} 53 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Define some default values that can be overridden by system properties 2 | Binlog.root.logger=INFO,console,DRFA 3 | Binlog.security.logger=INFO,console 4 | Binlog.log.dir=. 5 | Binlog.log.file=Binlog.log 6 | 7 | # Define the root logger to the system property Binlog.root.logger. 8 | log4j.rootLogger=${Binlog.root.logger} 9 | 10 | # Logging Threshold 11 | log4j.threshold=ALL 12 | 13 | # 14 | # Daily Rolling File Appender 15 | # 16 | log4j.appender.DRFA=org.apache.log4j.DailyRollingFileAppender 17 | log4j.appender.DRFA.Encoding=UTF-8 18 | log4j.appender.DRFA.File=${Binlog.log.dir}/${Binlog.log.file} 19 | 20 | # Rollver at midnight 21 | log4j.appender.DRFA.DatePattern=.yyyy-MM-dd 22 | 23 | # 30-day backup 24 | #log4j.appender.DRFA.MaxBackupIndex=30 25 | log4j.appender.DRFA.layout=org.apache.log4j.PatternLayout 26 | 27 | # Pattern format: Date LogLevel LoggerName LogMessage 28 | log4j.appender.DRFA.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2}: %m%n 29 | 30 | # Rolling File Appender properties 31 | Binlog.log.maxfilesize=256MB 32 | Binlog.log.maxbackupindex=20 33 | 34 | # Rolling File Appender 35 | log4j.appender.RFA=org.apache.log4j.RollingFileAppender 36 | log4j.appender.RFA.File=${Binlog.log.dir}/${Binlog.log.file} 37 | 38 | log4j.appender.RFA.MaxFileSize=${Binlog.log.maxfilesize} 39 | log4j.appender.RFA.MaxBackupIndex=${Binlog.log.maxbackupindex} 40 | 41 | log4j.appender.RFA.layout=org.apache.log4j.PatternLayout 42 | log4j.appender.RFA.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2}: %m%n 43 | 44 | # 45 | # Security audit appender 46 | # 47 | Binlog.security.log.file=SecurityAuth.audit 48 | Binlog.security.log.maxfilesize=256MB 49 | Binlog.security.log.maxbackupindex=20 50 | log4j.appender.RFAS=org.apache.log4j.RollingFileAppender 51 | log4j.appender.RFAS.File=${Binlog.log.dir}/${Binlog.security.log.file} 52 | log4j.appender.RFAS.MaxFileSize=${Binlog.security.log.maxfilesize} 53 | log4j.appender.RFAS.MaxBackupIndex=${Binlog.security.log.maxbackupindex} 54 | log4j.appender.RFAS.layout=org.apache.log4j.PatternLayout 55 | log4j.appender.RFAS.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n 56 | log4j.category.SecurityLogger=${Binlog.security.logger} 57 | log4j.additivity.SecurityLogger=false 58 | #log4j.logger.SecurityLogger.org.apache.hadoop.Binlog.security.access.AccessController=TRACE 59 | 60 | # 61 | # Null Appender 62 | # 63 | log4j.appender.NullAppender=org.apache.log4j.varia.NullAppender 64 | 65 | # 66 | # console 67 | # Add console to rootlogger above if you want to use this 68 | # 69 | log4j.appender.console=org.apache.log4j.ConsoleAppender 70 | log4j.appender.console.target=System.err 71 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 72 | log4j.appender.console.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2}: %m%n 73 | 74 | # Custom Logging levels 75 | 76 | log4j.com.wellcommsoft.noce=INFO 77 | 78 | -------------------------------------------------------------------------------- /src/test/java/com/bixuange/FileSystemTest.java: -------------------------------------------------------------------------------- 1 | package com.bixuange; 2 | 3 | import com.common.FSUtils; 4 | import org.apache.hadoop.conf.Configuration; 5 | import org.apache.hadoop.fs.*; 6 | import org.apache.hadoop.io.IOUtils; 7 | import sun.rmi.runtime.Log; 8 | 9 | import java.io.ByteArrayInputStream; 10 | import java.io.IOException; 11 | import java.io.OutputStream; 12 | import java.util.concurrent.Semaphore; 13 | 14 | public class FileSystemTest { 15 | 16 | public static void main(String[] args) throws Exception { 17 | // System.setProperty("HADOOP_USER_NAME", "hdfs"); 18 | FileSystem fs = null; 19 | try { 20 | //1527386243433 21 | fs = FSUtils.getNewFileSystem(); 22 | System.out.println(fs.exists(new Path("/DATA/PUBLIC/TABLE1/day=20180519/"))); 23 | Long lastModeifTime = Long.MIN_VALUE; 24 | Path path = null; 25 | for (FileStatus fileStatus: fs.listStatus(new Path("/DATA/PUBLIC/TABLE1/day=20180519/"))){ 26 | Long time = fileStatus.getModificationTime(); 27 | if (time > lastModeifTime){ 28 | lastModeifTime = time; 29 | path = fileStatus.getPath(); 30 | } 31 | } 32 | 33 | System.out.println(path.toString()); 34 | System.out.println(fs.getFileStatus(new Path("/DATA/PUBLIC/TABLE1/day=20180519/000000_0")).getLen()); 35 | 36 | // System.out.println(fs.getFileStatus(new Path("/mobin.txt")).getModificationTime()); 37 | // OutputStream out = FSUtils.openOutputStream(null, "/test/2.txt","",""); 38 | // out.write(("test。。。。。" + "\n").getBytes()); 39 | // out.flush(); 40 | } finally { 41 | // fs.close(); 42 | } 43 | } 44 | 45 | static class MyThread extends Thread { 46 | OutputStream out; 47 | Semaphore semaphore; 48 | 49 | public MyThread() { 50 | super("MyThread"); 51 | } 52 | 53 | @Override 54 | public void run() { 55 | try { 56 | for (int i = 0; i < 100; i++) { 57 | try { 58 | synchronized (out) { 59 | out.write((Thread.currentThread().getName() + "aaaaaaaaaaaaaaaaa" + i + "\n").getBytes()); 60 | } 61 | } catch (IOException e) { 62 | // TODO Auto-generated catch block 63 | e.printStackTrace(); 64 | } 65 | } 66 | try { 67 | synchronized (out) { 68 | out.flush(); 69 | } 70 | } catch (IOException e) { 71 | // TODO Auto-generated catch block 72 | e.printStackTrace(); 73 | } 74 | } finally { 75 | semaphore.release(); 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/com/bixuange/BinlogClientTest.java: -------------------------------------------------------------------------------- 1 | package com.bixuange; 2 | 3 | import com.common.BinlogOption; 4 | import com.common.FSUtils; 5 | import com.github.shyiko.mysql.binlog.BinaryLogFileReader; 6 | import com.github.shyiko.mysql.binlog.event.*; 7 | import com.github.shyiko.mysql.binlog.event.deserialization.ChecksumType; 8 | import com.github.shyiko.mysql.binlog.event.deserialization.EventDeserializer; 9 | import com.github.shyiko.mysql.binlog.event.deserialization.NullEventDataDeserializer; 10 | import org.apache.hadoop.fs.FileSystem; 11 | import org.junit.Test; 12 | 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.sql.SQLException; 16 | 17 | /** 18 | * Created with IDEA 19 | * Creater: MOBIN 20 | * Date: 2018/5/17 21 | * Time: 上午11:16 22 | */ 23 | public class BinlogClientTest { 24 | @Test 25 | public void binlogClinet() throws IOException { 26 | BinlogClient client = new BinlogClient(); 27 | FileSystem fs = null; 28 | try { 29 | fs = FSUtils.getFileSystem(); 30 | BinlogOption option = new BinlogOption(null); 31 | client.replicationStream(fs,option); 32 | } catch (SQLException e) { 33 | e.printStackTrace(); 34 | } finally { 35 | fs.close(); 36 | } 37 | 38 | } 39 | 40 | 41 | @Test 42 | public void readBinlog() throws IOException { 43 | File binlogFile = new File("/Users/mobin/Downloads/RDS-binlog/mysql-bin.000043"); 44 | EventDeserializer eventDeserializer = new EventDeserializer(); 45 | // eventDeserializer.setEventDataDeserializer(EventType.WRITE_ROWS, new ByteArrayEventDataDeserializer()); 46 | eventDeserializer.setEventDataDeserializer(EventType.UPDATE_ROWS, new NullEventDataDeserializer()); 47 | eventDeserializer.setChecksumType(ChecksumType.CRC32); 48 | 49 | BinaryLogFileReader reader = new BinaryLogFileReader(binlogFile,eventDeserializer); 50 | try { 51 | for (Event event; (event = reader.readEvent()) != null; ) { 52 | // && !EventHeaderV4.class.isInstance(event.getHeader()) 53 | EventType eventType = event.getHeader().getEventType();// TableMapEventData tableMapEventData = event.getData(); 54 | if (eventType == EventType.TABLE_MAP) { //判断是哪张表 55 | TableMapEventData tableMapEventData = event.getData(); 56 | System.out.println(tableMapEventData.getTable()); 57 | } 58 | // 59 | // if (event.getHeader().getEventType() == EventType.WRITE_ROWS){ 60 | // WriteRowsEventData writeRowsEventData = new WriteRowsEventData(); 61 | // writeRowsEventData.setRows(event.getData()); 62 | //// ByteArrayEventData byteArrayEventData = event.getData(); 63 | //// String s = new String(byteArrayEventData.getData()); 64 | //// System.out.println(s); 65 | // } 66 | } 67 | } finally { 68 | reader.close(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/common/BinlogOption.java: -------------------------------------------------------------------------------- 1 | package com.common; 2 | 3 | import com.config.DatabaseConnection; 4 | import org.apache.hadoop.fs.FileSystem; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import java.sql.Connection; 8 | import java.sql.PreparedStatement; 9 | import java.sql.ResultSet; 10 | import java.sql.SQLException; 11 | 12 | /** 13 | * Created with IDEA 14 | * Creater: MOBIN 15 | * Date: 2018/5/17 16 | * Time: 下午2:46 17 | */ 18 | public class BinlogOption { 19 | private static final Logger log = LoggerFactory.getLogger(BinlogOption.class); 20 | public String dirPath = null; 21 | public int count = 0; 22 | public int countInterval = 50000; //每50000写入一次 23 | public static String database = "mobinDB"; 24 | public Long binlogPosition = null; //自上次同步的游标开始同步 25 | public String binlogFileName = null; 26 | public FileSystem fs = null; 27 | public boolean isSyncTable = true; //是否同步该表 28 | 29 | public boolean syncByBinlogFile = false; //通过指定binlog进行同步,默认从数据库获取上次同步的游标 30 | public String binlogFilePath = null; 31 | 32 | public BinlogOption(String[] args){ 33 | for (int i = 0; i < args.length; i ++){ 34 | String arg = args[i]; 35 | switch (arg){ 36 | case "-syncByBinlogFile": 37 | syncByBinlogFile = Boolean.parseBoolean(args[++ i]); 38 | break; 39 | case "-countInterval": 40 | countInterval = Integer.parseInt(args[++ i]); 41 | break; 42 | case "-binlogFilePath": 43 | binlogFilePath = args[++ i]; 44 | break; 45 | default: 46 | log.error("参数无效:" + arg); 47 | System.exit(-1); 48 | } 49 | } 50 | if (!syncByBinlogFile){ 51 | getNextPosition(); 52 | }else { 53 | checkBinlogFilePath(binlogFilePath); 54 | } 55 | } 56 | 57 | private void checkBinlogFilePath(String binlogFilePath) { 58 | if (binlogFilePath == null){ 59 | log.error("binlogFilePath路径无效:{}",binlogFilePath); 60 | System.exit(-1); 61 | } 62 | } 63 | 64 | 65 | public void getNextPosition(){ 66 | Connection conn = null; 67 | PreparedStatement ps = null; 68 | ResultSet rs = null; 69 | try { 70 | conn = DatabaseConnection.getMysqlConnection(); 71 | String SQL = "SELECT nextposition,binlogfilename FROM t_position"; 72 | ps = conn.prepareStatement(SQL); 73 | rs = ps.executeQuery(); 74 | while (rs.next()) { 75 | binlogPosition = rs.getLong("nextposition"); 76 | binlogFileName = rs.getString("binlogfilename"); 77 | } 78 | log.info("数据库最新nextposition={},binlogfilename={}",binlogPosition,binlogFileName); 79 | } catch (SQLException e) { 80 | e.printStackTrace(); 81 | }finally { 82 | try { 83 | ps.close(); 84 | conn.close(); 85 | } catch (SQLException e) { 86 | e.printStackTrace(); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/test/java/com/bixuange/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.bixuange; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import com.common.ExecCMD; 6 | import com.common.FSUtils; 7 | import com.config.DatabaseConnection; 8 | import com.main.Binlog2Hive; 9 | import org.apache.hadoop.fs.FileSystem; 10 | import org.apache.hadoop.fs.Path; 11 | import org.apache.hadoop.metrics.spi.OutputRecord; 12 | import org.junit.Test; 13 | 14 | import javax.swing.text.html.parser.Entity; 15 | import java.io.IOException; 16 | import java.io.OutputStream; 17 | import java.sql.SQLException; 18 | import java.util.*; 19 | import java.util.concurrent.Semaphore; 20 | 21 | /** 22 | * Unit test for simple App. 23 | */ 24 | public class AppTest { 25 | @Test 26 | public void shouldAnswerWithTrue() throws IOException, SQLException { 27 | // System.out.println(FSUtils.getFileSystem()); 28 | System.out.println(DatabaseConnection.getMysqlConnection()); 29 | } 30 | 31 | @Test 32 | public void append() throws IOException { 33 | OutputStream out = null; 34 | FileSystem fs = null; 35 | try { 36 | fs = FSUtils.getNewFileSystem(); 37 | final Semaphore semaphore = new Semaphore(2); 38 | for (int i=0; i<=3 ; i ++){ 39 | out = FSUtils.openOutputStream(null,"",""); 40 | for (int j = 1000; j < 2000; j++) { 41 | out.write(("aaaaaaaaaaaaaaaaa" + j + "\n").getBytes()); 42 | } 43 | out.flush(); 44 | out.close(); 45 | out.close(); 46 | } 47 | } catch (IOException e) { 48 | e.printStackTrace(); 49 | } catch (Exception e) { 50 | e.printStackTrace(); 51 | } finally { 52 | out.close(); 53 | fs.close(); 54 | } 55 | } 56 | 57 | @Test 58 | public void split(){ 59 | String str = "/DATA/PUBLIC/TABLE1/day=20180514/000000_0"; 60 | String str1 = "T_ALLAWSMIND_20180518"; 61 | String tableName = str1.split("_",-1)[2]; 62 | System.out.println(tableName); 63 | 64 | System.out.println(new java.util.Date()); 65 | 66 | String fileName = "/DATA/PUBLIC/TABLE1/day=20180522/000000_0"; 67 | System.out.println(fileName.split("_",-1)[0]); 68 | System.out.println(Integer.parseInt(fileName.split("_",-1)[2])); 69 | } 70 | 71 | @Test 72 | public void process() throws IOException { 73 | Process proc=Runtime.getRuntime().exec("/opt/hive-1.2.2/bin/hive -e 'create table yy(id int)'"); 74 | // String SQL = "hive -e " + "USE met_office;ALTER TABLE T_HNAWSMIND add IF NOT EXISTS partition (day=20180519) location '/DATA/PUBLIC/METOFFICE/T_HNAWSMIND/day=20180519/'"; 75 | // String SQL = "open ."; 76 | // System.out.println(SQL); 77 | // ExecCMD.exec(SQL); 78 | } 79 | 80 | @Test 81 | public void getNextPosition() throws SQLException { 82 | // System.out.println(Binlog2Hive.getNextPosition(null)); 83 | HashMap map = new HashMap<>(); 84 | map.put("2",1); 85 | map.put("1",4); 86 | map.put("3",2); 87 | Set> set = map.entrySet(); 88 | List> list = new ArrayList<>(set); 89 | Collections.sort(list, new Comparator>() { 90 | @Override 91 | public int compare(Map.Entry o1, Map.Entry o2) { 92 | return o1.getValue() - o2.getValue(); 93 | } 94 | }); 95 | 96 | LinkedHashMap linkedHashMap = new LinkedHashMap<>(); 97 | for (Map.Entry entry : list){ 98 | linkedHashMap.put(entry.getKey(),entry.getValue()); 99 | } 100 | 101 | System.out.println(linkedHashMap); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/common/FSUtils.java: -------------------------------------------------------------------------------- 1 | package com.common; 2 | 3 | import org.apache.hadoop.conf.Configuration; 4 | import org.apache.hadoop.fs.FileStatus; 5 | import org.apache.hadoop.fs.FileSystem; 6 | import org.apache.hadoop.fs.Path; 7 | import org.apache.hadoop.io.IOUtils; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.io.*; 12 | 13 | public class FSUtils { 14 | private static final Logger log = LoggerFactory.getLogger(FSUtils.class); 15 | 16 | public static final int BLOCK_SIZE = 240; 17 | public static FileSystem getFileSystem() throws IOException { 18 | Configuration conf = new Configuration(); 19 | conf.set("fs.hdfs.impl",org.apache.hadoop.hdfs.DistributedFileSystem.class.getName()); 20 | return FileSystem.get(conf); 21 | } 22 | 23 | public static FileSystem getNewFileSystem() throws IOException { 24 | Configuration conf = new Configuration(); 25 | conf.set("fs.hdfs.impl",org.apache.hadoop.hdfs.DistributedFileSystem.class.getName()); 26 | return FileSystem.newInstance(conf); 27 | } 28 | 29 | // public static OutputStream createLzoOutputStream(FileSystem fs, Path lzoFile) throws IOException { 30 | // OutputStream os = fs.create(lzoFile); 31 | // return createLzoOutputStream(fs, os); 32 | // } 33 | // 34 | // public static OutputStream createLzoOutputStream(FileSystem fs, OutputStream os) throws IOException { 35 | // 36 | //// OutputStream os = fs.create(lzoFile); 37 | // return createLzoOutputStream(fs, os); 38 | // } 39 | 40 | public static OutputStream openOutputStream(FileSystem fs, String day, String tableNameDayPath) throws Exception { 41 | OutputStream os = null; 42 | Path path = getLastModifiedFile(fs,new Path(tableNameDayPath)); //获取最近写入的那个文件 43 | if (fs.exists(path) && fs.getFileStatus(path).getLen()/(1<<20) <= BLOCK_SIZE) { 44 | log.info("append内容到{}",path); 45 | try { 46 | os = fs.append(path); 47 | log.info(path+"文件append完成"); 48 | } catch (Exception e) { 49 | log.error("文件append失败:" + e); 50 | //在不支持append的情况下,先将数据读出来,再写回去 51 | byte[] oldBytes = FSUtils.readDataFile(fs, path); //追加的文件内容 52 | os = fs.create(path); //被追加文件内容 53 | os.write(oldBytes); 54 | } 55 | } else if (!fs.exists(path)) { 56 | log.info("创建{}文件", path); //DATA/PUBLIC/TABLE1/day=20180522/000000_0 57 | os = fs.create(path); 58 | log.info("开始对{}进行分区", tableNameDayPath); 59 | addHivePartition(path.toString(), day, tableNameDayPath); 60 | } else{ 61 | int fileName_suffix = Integer.parseInt(path.getName().split("_",-1)[1]) + 1; 62 | String newFilePath = tableNameDayPath + "/" + "000000_" + fileName_suffix; 63 | log.info("创建{}文件",newFilePath); 64 | os = fs.create(new Path(newFilePath)); 65 | } 66 | return os; 67 | } 68 | 69 | public static Path getLastModifiedFile(FileSystem fs,Path path) throws IOException { 70 | if (!fs.exists(path) || !fs.exists(new Path(path.toString() + "/000000_0"))){ 71 | return new Path(path.toString() + "/000000_0"); 72 | } 73 | Long lastModifiedTime = Long.MIN_VALUE; 74 | Path filePath = null; 75 | for (FileStatus fileStatus: fs.listStatus(path)){ 76 | Long lastTime = fileStatus.getModificationTime(); 77 | if (lastTime > lastModifiedTime){ 78 | lastModifiedTime = lastTime; 79 | filePath = fileStatus.getPath(); 80 | } 81 | } 82 | return filePath; 83 | } 84 | 85 | 86 | public static void addHivePartition(String path, String day,String tableName_day) { 87 | StringBuffer sb = new StringBuffer(); 88 | String tableName = path.split("/", -1)[4]; 89 | String location = tableName_day +"/"; 90 | sb.append("USE mobinDB;"); 91 | sb.append("ALTER TABLE ") 92 | .append(tableName) 93 | .append(" add IF NOT EXISTS partition ("); 94 | sb.append("day=").append(day).append(")"); 95 | sb.append(" location '").append(location).append("'"); 96 | String HiveSQL = String.format("hive -e \"%s\"", sb.toString()); 97 | System.out.println(HiveSQL); 98 | log.info(HiveSQL); 99 | ExecCMD.exec(HiveSQL); 100 | } 101 | 102 | public static byte[] readDataFile(FileSystem fs, Path dataFile) throws IOException { 103 | ByteArrayOutputStream bos = new ByteArrayOutputStream(2 * 1024 * 1024); 104 | InputStream in = null; 105 | try { 106 | in = fs.open(dataFile); 107 | IOUtils.copyBytes(in, bos, 4096, false); 108 | } finally { 109 | IOUtils.closeStream(in); 110 | } 111 | return bos.toByteArray(); 112 | } 113 | 114 | 115 | } 116 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | 8 | 9 | cloudera 10 | https://repository.cloudera.com/artifactory/cloudera-repos/ 11 | 12 | 13 | 14 | com.bixuange 15 | Binlog2Hive 16 | 1.0-SNAPSHOT 17 | 18 | Binlog2Hive 19 | 20 | http://www.example.com 21 | 22 | 23 | UTF-8 24 | 1.7 25 | 1.7 26 | 2.6.0-cdh5.7.2 27 | 28 | 29 | 30 | 31 | junit 32 | junit 33 | 4.11 34 | test 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | mysql 51 | mysql-connector-java 52 | 5.1.32 53 | 54 | 55 | 56 | com.alibaba 57 | druid 58 | 1.1.5 59 | 60 | 61 | 62 | com.github.shyiko 63 | mysql-binlog-connector-java 64 | 0.16.1 65 | 66 | 67 | 68 | org.apache.hadoop 69 | hadoop-common 70 | ${hadoop.version} 71 | 72 | 73 | org.apache.hadoop 74 | hadoop-hdfs 75 | ${hadoop.version} 76 | 77 | 78 | org.apache.hadoop 79 | hadoop-client 80 | ${hadoop.version} 81 | 82 | 83 | 84 | 85 | 86 | 87 | org.springframework.boot 88 | spring-boot-maven-plugin 89 | 90 | 91 | org.apache.maven.plugins 92 | maven-assembly-plugin 93 | 2.5.5 94 | 95 | 96 | 97 | Binlog2Hive 98 | 99 | 100 | 101 | jar-with-dependencies 102 | 103 | 104 | 105 | 106 | maven-compiler-plugin 107 | 108 | 1.8 109 | 1.8 110 | UTF-8 111 | 112 | ${project.basedir}/lib 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | maven-clean-plugin 121 | 3.0.0 122 | 123 | 124 | 125 | maven-resources-plugin 126 | 3.0.2 127 | 128 | 129 | maven-compiler-plugin 130 | 3.7.0 131 | 132 | 133 | maven-surefire-plugin 134 | 2.20.1 135 | 136 | 137 | maven-jar-plugin 138 | 3.0.2 139 | 140 | 141 | maven-install-plugin 142 | 2.5.2 143 | 144 | 145 | maven-deploy-plugin 146 | 2.8.2 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /src/main/java/com/bixuange/BinlogClient.java: -------------------------------------------------------------------------------- 1 | package com.bixuange; 2 | 3 | import com.common.BinlogOption; 4 | import com.common.FSUtils; 5 | import com.config.Config; 6 | import com.config.DatabaseConnection; 7 | import com.github.shyiko.mysql.binlog.BinaryLogClient; 8 | import com.github.shyiko.mysql.binlog.BinaryLogFileReader; 9 | import com.github.shyiko.mysql.binlog.event.*; 10 | import com.github.shyiko.mysql.binlog.event.deserialization.ChecksumType; 11 | import com.github.shyiko.mysql.binlog.event.deserialization.EventDeserializer; 12 | import com.github.shyiko.mysql.binlog.event.deserialization.NullEventDataDeserializer; 13 | import org.apache.hadoop.fs.FileSystem; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.io.*; 18 | import java.nio.charset.Charset; 19 | import java.sql.Connection; 20 | import java.sql.PreparedStatement; 21 | import java.sql.SQLException; 22 | import java.text.SimpleDateFormat; 23 | import java.util.*; 24 | import java.util.concurrent.ConcurrentHashMap; 25 | 26 | /** 27 | * Created with IDEA 28 | * Creater: MOBIN 29 | * Date: 2018/5/17 30 | * Time: 上午10:08 31 | */ 32 | public class BinlogClient { 33 | private static final Logger log = LoggerFactory.getLogger(BinlogClient.class); 34 | private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 35 | private SimpleDateFormat sfDay = new SimpleDateFormat("yyyyMMdd"); 36 | 37 | public ConcurrentHashMap> tableRows = new ConcurrentHashMap(); //key是表前缀路径 38 | private static final String HOSTNAME = "localhost"; 39 | private static final int PORT = 3306; 40 | private static final String USERNAME = "root"; 41 | private static final String PASSWD = "root"; 42 | 43 | public void replicationStream(FileSystem fs, final BinlogOption option) throws IOException, SQLException { 44 | BinaryLogClient client = new BinaryLogClient(HOSTNAME, PORT, USERNAME, PASSWD); 45 | if (!option.syncByBinlogFile){ 46 | client.setBinlogFilename(option.binlogFileName); 47 | client.setBinlogPosition(option.binlogPosition); 48 | } 49 | log.info("即将解析{}文件,起始游标为:{}", option.binlogFileName, option.binlogPosition); 50 | client.registerEventListener(new BinaryLogClient.EventListener() { 51 | @Override 52 | public void onEvent(Event event) { 53 | analysisEvent(event, option, fs, true); 54 | } 55 | }); 56 | client.connect(); 57 | } 58 | 59 | 60 | public void readDataFromBinlogFile(String filePath, FileSystem fs, final BinlogOption option) throws IOException { 61 | log.info("正在解析{}文件",filePath); 62 | File binlogFile = new File(filePath); 63 | EventDeserializer eventDeserializer = new EventDeserializer(); 64 | eventDeserializer.setEventDataDeserializer(EventType.UPDATE_ROWS, new NullEventDataDeserializer()); 65 | eventDeserializer.setEventDataDeserializer(EventType.DELETE_ROWS, new NullEventDataDeserializer()); 66 | eventDeserializer.setChecksumType(ChecksumType.CRC32); 67 | BinaryLogFileReader reader = new BinaryLogFileReader(binlogFile, eventDeserializer); 68 | try { 69 | for (Event event; (event = reader.readEvent()) != null; ) { 70 | analysisEvent(event, option, fs, false); 71 | } 72 | } finally { 73 | reader.close(); 74 | } 75 | } 76 | 77 | private void analysisEvent(Event event, BinlogOption option, FileSystem fs, boolean isUpdatePosition) { 78 | EventType eventType = event.getHeader().getEventType(); 79 | if (eventType == EventType.ROTATE) { 80 | RotateEventData rotateEventData = event.getData(); 81 | option.binlogFileName = rotateEventData.getBinlogFilename(); 82 | } 83 | if (eventType == EventType.TABLE_MAP) { //判断是哪张表 84 | TableMapEventData tableMapEventData = event.getData(); 85 | String tableName = tableMapEventData.getTable().toLowerCase(); 86 | String database = tableMapEventData.getDatabase().toLowerCase(); 87 | String dirPath = Config.getPath(tableName); 88 | option.isSyncTable = dirPath == null ? false : true; 89 | option.database = database; 90 | option.dirPath = dirPath; 91 | } 92 | if (option.isSyncTable && "mobinDB".equals(option.database) && eventType == EventType.WRITE_ROWS ) { 93 | List writeRowsEventDataList = tableRows.get(option.dirPath); 94 | if (writeRowsEventDataList == null) { 95 | writeRowsEventDataList = new ArrayList<>(); 96 | tableRows.put(option.dirPath, writeRowsEventDataList); 97 | } 98 | WriteRowsEventData writeRowsEventData = event.getData(); 99 | option.count = option.count + writeRowsEventData.getRows().size(); 100 | writeRowsEventDataList.addAll(writeRowsEventData.getRows()); 101 | } 102 | if (option.count > option.countInterval) { 103 | ConcurrentHashMap> map = new ConcurrentHashMap<>();//key是表名+分区目录名(day),value是一条数据 104 | try { 105 | Iterator iterators = tableRows.keySet().iterator(); 106 | //不同表且分区字段不一样打开一次文件流 107 | while (iterators.hasNext()) { //遍历次数少,根据表前缀路径来遍历 108 | traverseTable(map, iterators, option); //将数据按天进行分区 109 | } 110 | log.info("数据整理完毕,准备写入HDFS"); 111 | Iterator its = map.keySet().iterator(); 112 | while (its.hasNext()) { //遍历次数少,根据表的分区字段day遍历 113 | traverseDay(event, map, its, fs, option, isUpdatePosition); 114 | } 115 | } catch (IOException e) { 116 | e.printStackTrace(); 117 | } catch (Exception e) { 118 | e.printStackTrace(); 119 | } 120 | log.info("本次同步{}条数据", option.count); 121 | option.count = 0; 122 | tableRows.clear(); 123 | } 124 | } 125 | 126 | 127 | private void traverseDay(Event event, ConcurrentHashMap> map, 128 | Iterator its, FileSystem fs, 129 | BinlogOption option, boolean isUpdatePosition) throws Exception { 130 | OutputStream out; 131 | String tableName_day = its.next(); 132 | String day = tableName_day.split("=", -1)[1]; 133 | out = FSUtils.openOutputStream(fs, day, tableName_day); 134 | for (Serializable[] row : map.get(tableName_day)) { 135 | StringBuffer sb = new StringBuffer(); 136 | for (int i = 0; i < row.length; i++) { 137 | if (i == row.length - 1) { 138 | sb.append(row[i]); 139 | } else if (i == 1) { 140 | synchronized (sdf) { 141 | sdf.setTimeZone(TimeZone.getTimeZone("GTM")); 142 | sb.append(sdf.format(row[1]) + "|"); 143 | } 144 | } else { 145 | sb.append(row[i] + "|"); 146 | } 147 | } 148 | sb.append("\n"); 149 | out.write(sb.toString().getBytes(Charset.forName("UTF-8"))); 150 | } 151 | out.flush(); 152 | out.close(); 153 | if (isUpdatePosition) { 154 | EventHeaderV4 eventHeaderV4 = event.getHeader(); 155 | option.binlogPosition = eventHeaderV4.getNextPosition(); 156 | updateBinlogNextPosition(option); 157 | } 158 | 159 | } 160 | 161 | private void traverseTable(ConcurrentHashMap> map, Iterator iterators, BinlogOption option) { 162 | String table_path_prefix = iterators.next(); 163 | String tableName = table_path_prefix.split("/", -1)[4]; 164 | List rows = tableRows.get(table_path_prefix); 165 | for (Serializable[] row : rows) { 166 | String datetime; 167 | synchronized (sfDay) { 168 | sfDay.setTimeZone(TimeZone.getTimeZone("GTM")); 169 | datetime = sfDay.format(row[1]); 170 | } 171 | String key = table_path_prefix + "/day=" + datetime; 172 | ArrayList rowList = map.get(key); 173 | if (rowList == null) { 174 | rowList = new ArrayList<>(); 175 | map.put(key, rowList); 176 | } 177 | rowList.add(row); 178 | } 179 | } 180 | 181 | public static void updateBinlogNextPosition(BinlogOption option) { 182 | Connection conn = null; 183 | PreparedStatement ps = null; 184 | try { 185 | conn = DatabaseConnection.getMysqlConnection(); 186 | String SQL = "UPDATE t_position SET nextposition=?,binlogfilename=?"; 187 | ps = conn.prepareStatement(SQL); 188 | ps.setLong(1, option.binlogPosition); 189 | ps.setString(2, option.binlogFileName); 190 | ps.executeUpdate(); 191 | ps.close(); 192 | log.info("更新游标为:{},binlogFileName文件为{}", option.binlogPosition, option.binlogFileName); 193 | } catch (SQLException e) { 194 | e.printStackTrace(); 195 | } finally { 196 | try { 197 | if (conn != null) { 198 | conn.close(); 199 | } 200 | } catch (SQLException e) { 201 | e.printStackTrace(); 202 | } 203 | } 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /Binlog2Hive.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | --------------------------------------------------------------------------------