mysql binlog系列(二)----java解析binlog

5 篇文章 3 订阅

在进入正题之前,我们需要知道binlog的event的类型,先来看看自己binlog文件有哪些?


其中红色部分为event_type。


binlog event 的类型有很多,具体可以参见mysql官方文档:http://dev.mysql.com/doc/internals/en/event-meanings.html


(一)Open Replicator中相关的Event类与接口

Open Replicator是一个用Java编写的MySQL binlog分析程序。Open Replicator 首先连接到MySQL(就像一个普通的MySQL Slave一样),然后接收和分析binlog,最终将分析得出的binlog events以回调的方式通知应用。


所有的Event实现了BinlogEventV4接口。


BinlogEventV4的接口如下:

/**
 * +=====================================+
 * | event  | timestamp         0 : 4    |
 * | header +----------------------------+
 * |        | type_code         4 : 1    |
 * |        +----------------------------+
 * |        | server_id         5 : 4    |
 * |        +----------------------------+
 * |        | event_length      9 : 4    |
 * |        +----------------------------+
 * |        | next_position    13 : 4    |
 * |        +----------------------------+
 * |        | flags            17 : 2    |
 * +=====================================+
 * | event  | fixed part       19 : y    |
 * | data   +----------------------------+
 * |        | variable part              |
 * +=====================================+
 * @author Jingqi Xu
 */
public interface BinlogEventV4 {
	
	BinlogEventV4Header getHeader();
}

(二)利用Open Replicator解析binlog

在这里首先申明本人的测试环境为:mysql 5.1.61 ,binlog的类型设置为Row,本次解析只考虑insert、update、delete三种事件类型。我们先将三种类型的时间包装为一个新的Event,如下所示:

public class LogEvent implements Serializable{

	/**
	 * 只针对delete、insert、update事件
	 */
	private static final long serialVersionUID = 5503152746318421290L;
	
	private String eventId = null;
	private String databaseName = null;
	private String tableName = null;
	private String  eventType  = null;
    private Long    timestamp  = null;
    private Long timestampRecepite = null;
    private String binlogName = null;
    private Long position = null;
    private Long nextPosition = null;
    private Long serverId = null;
    private Map<String, String> before =null;
    private Map<String, String> after = null;
    
    public LogEvent(){
    	
    }
    
    public LogEvent(final QueryEvent qe,String databaseName,String tableName){
    	this.init(qe);
    	this.databaseName=databaseName;
    	this.tableName=tableName;	
    }
    
    public LogEvent(final AbstractRowEvent re){
    	this.init(re);
    	TableMapEvent tableMapEvent =re.getTme();
    	this.databaseName=tableMapEvent.getDatabaseName().toString();
    	this.tableName=tableMapEvent.getTableName().toString();	
    }
    
    private void init(final BinlogEventV4 be){
    	this.eventId=UUID.randomUUID().toString();
    	BinlogEventV4Header header = be.getHeader();
    	this.binlogName = header.getBinlogName();
    	this.position = header.getPosition();
    	this.nextPosition = header.getNextPosition();
    	this.timestamp = header.getTimestamp();
    	this.timestampRecepite = header.getTimestampOfReceipt();
    	this.serverId=header.getServerId();
    	this.eventType=MySqlEventTypeIdToString.getInstance().get(header.getEventType());
    }
    
    

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("{ eventId:").append(eventId);
		builder.append(",databaseName:").append(databaseName);
		builder.append(",tableName:").append(tableName);
		builder.append(",eventType:").append(eventType);
		builder.append(",timestamp:").append(timestamp);
		builder.append(",timestampRecepite:").append(timestampRecepite);
		builder.append(",binlogName:").append(binlogName);
		builder.append(",position:").append(position);
		builder.append(",nextPosition:").append(nextPosition);
		builder.append(",serverId:").append(serverId);
		builder.append(",before:").append(before);
		builder.append(",after:").append(after).append(" }");
		return builder.toString();
	}



	public String getEventId() {
		return eventId;
	}

	public void setEventId(String eventId) {
		this.eventId = eventId;
	}

	public String getDatabaseName() {
		return databaseName;
	}

	public void setDatabaseName(String databaseName) {
		this.databaseName = databaseName;
	}

	public String getTableName() {
		return tableName;
	}

	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	public String getEventType() {
		return eventType;
	}

	public void setEventType(String eventType) {
		this.eventType = eventType;
	}

	public Long getTimestamp() {
		return timestamp;
	}

	public void setTimestamp(Long timestamp) {
		this.timestamp = timestamp;
	}

	public Long getTimestampRecepite() {
		return timestampRecepite;
	}

	public void setTimestampRecepite(Long timestampRecepite) {
		this.timestampRecepite = timestampRecepite;
	}

	public String getBinlogName() {
		return binlogName;
	}

	public void setBinlogName(String binlogName) {
		this.binlogName = binlogName;
	}

	public Long getPosition() {
		return position;
	}

	public void setPosition(Long position) {
		this.position = position;
	}

	public Long getNextPosition() {
		return nextPosition;
	}

	public void setNextPosition(Long nextPosition) {
		this.nextPosition = nextPosition;
	}

	public Long getServerId() {
		return serverId;
	}

	public void setServerId(Long serverId) {
		this.serverId = serverId;
	}

	public Map<String, String> getBefore() {
		return before;
	}

	public void setBefore(Map<String, String> before) {
		this.before = before;
	}

	public Map<String, String> getAfter() {
		return after;
	}

	public void setAfter(Map<String, String> after) {
		this.after = after;
	}

其中 before、after为一个map,表示变化前后所在行的所有数据(columnName:columnValue)!


好的,先上主程序:

public class OpenReplicatorTest {
	public static void main(String args[]) throws Exception {	
		final OpenReplicator or = new OpenReplicator();
		or.setUser("root");
		or.setPassword("root");
		or.setHost("xx.xxx.xx.xx");
		or.setPort(3306);
		or.setServerId(23);
		or.setBinlogPosition(106);
		or.setBinlogFileName("mysql-bin.000001");
		
		or.setBinlogEventListener(new NotificationListener());
		or.start();
		}
	}

设置监控器NotificationListener, NotificationListener需要实现BinlogEventListener接口:

public class NotificationListener implements BinlogEventListener{
	
	private static Logger logger = LoggerFactory.getLogger(NotificationListener.class);
    
	private String host="xx.xx.xx.xx";
	private Integer port=3306;
	private String username="root";
	private String password="root";
	
	public void onEvents(BinlogEventV4 event) {
		if(event==null){
			logger.error("binlog event is null");
			return;
		}

		if(event instanceof UpdateRowsEvent){
			UpdateRowsEvent updateRowsEvent = (UpdateRowsEvent)event;
			LogEvent logEvent = new LogEvent(updateRowsEvent);
			
			List<Pair<Row>> rows = updateRowsEvent.getRows();
			List<Column> cols_after = null;
			List<Column> cols_before = null;
			for(Pair<Row> p : rows){
				 Row after = p.getAfter();
				 Row before = p.getBefore();
				 cols_after = after.getColumns();
				 cols_before = before.getColumns();
				 break;
			}
			logEvent.setBefore(getMap(cols_before, updateRowsEvent.getTme().getDatabaseName().toString(), updateRowsEvent.getTme().getTableName().toString()));
			logEvent.setAfter(getMap(cols_after, updateRowsEvent.getTme().getDatabaseName().toString(), updateRowsEvent.getTme().getTableName().toString()));
			logger.info("update event is:"+logEvent);
		}else if(event instanceof DeleteRowsEvent){
			DeleteRowsEvent deleteRowsEvent = (DeleteRowsEvent)event;
			LogEvent logEvent = new LogEvent(deleteRowsEvent);
			List<Row> rows = deleteRowsEvent.getRows();
			List<Column> before = null;
			for(Row row:rows){
				before = row.getColumns();
				break;
			}
			logEvent.setBefore(getMap(before, deleteRowsEvent.getTme().getDatabaseName().toString(), deleteRowsEvent.getTme().getTableName().toString()));
			logger.info("delete event is:"+logEvent);
			
		}else if(event instanceof WriteRowsEvent){
			WriteRowsEvent wrtiteRowsEvent = (WriteRowsEvent)event;
			LogEvent logEvent = new LogEvent(wrtiteRowsEvent);
			List<Row> rows = wrtiteRowsEvent.getRows();
			List<Column> before = null;
			for(Row row:rows){
				before = row.getColumns();
				break;
			}
			logEvent.setAfter(getMap(before, wrtiteRowsEvent.getTme().getDatabaseName().toString(), wrtiteRowsEvent.getTme().getTableName().toString()));
			logger.info("write event is:"+logEvent);
			
		}	
	}
	
	private Map<String, String> getMap(List<Column> cols,String databaseName,String tableName){
		if(cols==null||cols.size()==0){
			return null;
		}
		List<String> columnNames = new TableInfo(host,username,password, port).getColumns(databaseName, tableName);
	    if(columnNames==null){
	    	return null;
	    }
	    if(columnNames.size()!=cols.size()){
	    	logger.error("the size does not match...");
	    	return null;
	    }
	    Map<String, String> map = new HashMap<String, String>();
	    for(int i=0;i<columnNames.size();i++){
	    	if(cols.get(i).getValue()==null){
	    		map.put(columnNames.get(i).toString(),"");
	    	}else{
	    		map.put(columnNames.get(i).toString(),cols.get(i).toString());
	    	}
	    	
	    }
	    return map;
	}

由于 Open Replicator提供的Event中不包含数据库表中所有字段column name的信息,DeleteRowsEvent、UpdateRowsEvent、WriteRowsEvent包含变化前后的字段column value信息,而我们需要将其组合成before与after,因此需要想办法获取column names:

public class TableInfo {
	private static Logger logger = LoggerFactory.getLogger(TableInfo.class);
	
	/**
	 * key:databaseName+""+tableName
	 * value:columns name
	 */
	private static Map<String, List<String>> columnsMap = new HashMap<String, List<String>>();
	private String host;
	private Integer port;
	private String username;
	private String password;
	
	public TableInfo(String host,String username,String password,Integer port){
		this.host=host;
		this.username=username;
		this.password=password;
		this.port = port;
		if(columnsMap==null||columnsMap.size()==0){
			MysqlConnection.setConnection(this.host,this.port,this.username,this.password);
		    columnsMap = MysqlConnection.getColumns();
		}
	}
	
	public Map<String, List<String>> getMap(){
		return columnsMap;
	}
	
	public List<String> getColumns(String databaseName,String tableName){
		if(StringUtils.isNullOrEmpty(databaseName)||StringUtils.isNullOrEmpty(tableName)){
			return null;
		}
		String key = databaseName + "."+tableName;
		List<String> list =null;
		if(columnsMap.size()==0){
			MysqlConnection.setConnection(this.host,this.port,this.username,this.password);
		    columnsMap = MysqlConnection.getColumns();
			list = columnsMap.get(key);
		}else{
			list=columnsMap.get(key);
			if(list==null||list.size()==0){
				MysqlConnection.setConnection(this.host,this.port,this.username,this.password);
				columnsMap = MysqlConnection.getColumns();
				list = columnsMap.get(key);
			}
			
		}
		return list;
	}


MysqlConnection实现类如下:

public class MysqlConnection {

	private static Connection conn;

	private static final Logger logger = LoggerFactory.getLogger(MysqlConnection.class);
	private static String host;
	private static Integer port;
	private static String user;
	private static String password;

	public static void setConnection(String mySQLHost, Integer mySQLPort, String mySQLUser,
			String mySQLPassword) {
		try {
			if (conn == null || conn.isClosed()) {
				Class.forName("com.mysql.jdbc.Driver");

				conn = DriverManager.getConnection("jdbc:mysql://" + mySQLHost + ":" + mySQLPort
						+ "/", mySQLUser, mySQLPassword);
				logger.info("connected to mysql:{} : {}", mySQLHost, mySQLPort);
				host = mySQLHost;
				port = mySQLPort;
				user = mySQLUser;
				password = mySQLPassword;
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}

	public static Connection getConnection() {
		try {
			if (conn == null || conn.isClosed()) {
				setConnection(host, port, user, password);
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return conn;
	}

	public static Map<String, List<String>> getColumns(){
		Map<String, List<String>> cols = new HashMap<String, List<String>>();
		Connection conn = getConnection();
		try {
			DatabaseMetaData metaData = conn.getMetaData();
			ResultSet r = metaData.getCatalogs();
			String tableType[] = { "TABLE" };
			while (r.next()) {
				String databaseName = r.getString("TABLE_CAT");
				ResultSet result = metaData.getTables(databaseName, null, null, tableType);
				while (result.next()) {
					String tableName = result.getString("TABLE_NAME");
					String key = databaseName + "." + tableName;
					ResultSet colSet = metaData.getColumns(databaseName, null, tableName, null);
					cols.put(key, new ArrayList<String>());
					while (colSet.next()) {
						String column = colSet.getString("COLUMN_NAME");
						cols.get(key).add(column);
					}
				}
			}

		} catch (SQLException e) {
			logger.error(e.getMessage(), e);
			return null;
		}
		return cols;
	}
}

辅助类,根据event id获取event type:

public class MySqlEventTypeIdToString {
    private static Map<Integer, String> idToString = new HashMap<Integer, String>();
    private MySqlEventTypeIdToString() {
        Init();
    }
    public static MySqlEventTypeIdToString getInstance() {
        return m;
    }
    private void Init() {
        idToString.put(0,"UNKNOWN_EVENT");
        idToString.put(1,"START_EVENT_V3");
        idToString.put(2,"QUERY_EVENT");
        idToString.put(3,"STOP_EVENT");
        idToString.put(4,"ROTATE_EVENT");
        idToString.put(5,"INTVAR_EVENT");
        idToString.put(6,"LOAD_EVENT");
        idToString.put(7,"SLAVE_EVENT");
        idToString.put(8,"CREATE_FILE_EVENT");
        idToString.put(9,"APPEND_BLOCK_EVENT");
        idToString.put(10,"EXEC_LOAD_EVENT");
        idToString.put(11,"DELETE_FILE_EVENT");
        idToString.put(12,"NEW_LOAD_EVENT");
        idToString.put(13,"RAND_EVENT");
        idToString.put(14,"USER_VAR_EVENT");
        idToString.put(15,"FORMAT_DESCRIPTION_EVENT");
        idToString.put(16,"XID_EVENT");
        idToString.put(17,"BEGIN_LOAD_QUERY_EVENT");
        idToString.put(18,"EXECUTE_LOAD_QUERY_EVENT");
        idToString.put(19,"TABLE_MAP_EVENT");
        idToString.put(20,"PRE_GA_WRITE_ROWS_EVENT");
        idToString.put(21,"PRE_GA_UPDATE_ROWS_EVENT");
        idToString.put(22,"PRE_GA_DELETE_ROWS_EVENT");
        idToString.put(23,"WRITE_ROWS_EVENT");
        idToString.put(24,"UPDATE_ROWS_EVENT");
        idToString.put(25,"DELETE_ROWS_EVENT");
        idToString.put(26,"INCIDENT_EVENT");
        idToString.put(27,"HEARTBEAT_LOG_EVENT");
        idToString.put(28,"IGNORABLE_LOG_EVENT");
        idToString.put(29,"ROWS_QUERY_LOG_EVENT");
        idToString.put(30,"WRITE_ROWS_EVENT_V2");
        idToString.put(31,"UPDATE_ROWS_EVENT_V2");
        idToString.put(32,"DELETE_ROWS_EVENT_V2");
        idToString.put(33,"GTID_LOG_EVENT");
        idToString.put(34,"ANONYMOUS_GTID_LOG_EVENT");
        idToString.put(35,"PREVIOUS_GTIDS_LOG_EVENT");
    }
    public String get(Integer eventId) {
        return idToString.get(eventId);
    }
}

运行:

update event is: {
    eventId: a7acc3d0-7721-4ffe-84d4-4c2b7db5423a,
    databaseName: test,
    tableName: task,
    eventType: UPDATE_ROWS_EVENT,
    timestamp: 1450753740000,
    timestampRecepite: 1450887259271,
    binlogName: mysql-bin.000001,
    position: 248,
    nextPosition: 358,
    serverId: 23,
    before: {
        id=791,
        user_name=123,
        topology_path=,
        update_time=2015-08-05 10:53:57.0,
        status=1,
        department=,
        name=user01,
        create_time=2015-12-21 19:30:36.0,
        user_id=-1
    },
    after: {
        id=791,
        user_name=123,
        topology_path=,
        update_time=2015-08-05 10:53:57.0,
        status=2,
        department=,
        name=user02,
        create_time=2015-12-22 11:09:00.0,
        user_id=-1
    }
}







  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
注:下文中的 *** 代表文件名中的组件名称。 # 包含: 中文-英文对照文档:【***-javadoc-API文档-中文(简体)-英语-对照版.zip】 jar包下载地址:【***.jar下载地址(官方地址+国内镜像地址).txt】 Maven依赖:【***.jar Maven依赖信息(可用于项目pom.xml).txt】 Gradle依赖:【***.jar Gradle依赖信息(可用于项目build.gradle).txt】 源代码下载地址:【***-sources.jar下载地址(官方地址+国内镜像地址).txt】 # 本文件关键字: 中文-英文对照文档,中英对照文档,java,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册 # 使用方法: 解压 【***.jar中文文档.zip】,再解压其中的 【***-javadoc-API文档-中文(简体)版.zip】,双击 【index.html】 文件,即可用浏览器打开、进行查看。 # 特殊说明: ·本文档为人性化翻译,精心制作,请放心使用。 ·本文档为双语同时展示,一行原文、一行译文,可逐行对照,避免了原文/译文来回切换的麻烦; ·有原文可参照,不再担心翻译偏差误导; ·边学技术、边学英语。 ·只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; ·不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 # 温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件;
要使用MySQL JDBC驱动解析MySQL binlog,可以使用下面的步骤: 1.添加MySQL JDBC驱动:在pom.xml文件中添加以下依赖项: ```xml <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.25</version> </dependency> ``` 2.编写代码:使用JDBC连接到MySQL数据库,并获取binlog事件流。可以使用以下示例代码: ```java import java.io.IOException; import java.sql.*; import com.github.shyiko.mysql.binlog.BinaryLogClient; import com.github.shyiko.mysql.binlog.event.*; public class BinlogParser { public static void main(String[] args) throws IOException { BinaryLogClient client = new BinaryLogClient("localhost", 3306, "username", "password"); client.registerEventListener(event -> { EventData data = event.getData(); if (data instanceof WriteRowsEventData) { WriteRowsEventData writeRowsEventData = (WriteRowsEventData) data; System.out.println(writeRowsEventData.getRows()); } else if (data instanceof UpdateRowsEventData) { UpdateRowsEventData updateRowsEventData = (UpdateRowsEventData) data; System.out.println(updateRowsEventData.getRows()); } else if (data instanceof DeleteRowsEventData) { DeleteRowsEventData deleteRowsEventData = (DeleteRowsEventData) data; System.out.println(deleteRowsEventData.getRows()); } }); client.connect(); } } ``` 3.运行代码:启动应用程序并运行binlog事件监听器。这将输出所有写入、更新和删除事件的行数据。 以上就是使用MySQL JDBC驱动解析MySQL binlog的基本步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值