/* * Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software;Designed and Developed mainly by many Chinese * opensource volunteers. you can redistribute it and/or modify it under the * terms of the GNU General Public License version 2 only, as published by the * Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Any questions about this component can be directed to it's project Web address * https://code.google.com/p/opencloudb/. * */ package io.mycat.backend.heartbeat; import io.mycat.backend.MySQLDataSource; import io.mycat.server.interceptor.impl.GlobalTableUtil; import io.mycat.sqlengine.OneRawSQLQueryResultHandler; import io.mycat.sqlengine.SQLJob; import io.mycat.sqlengine.SQLQueryResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSON; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; /** * @author digdeep@126.com */ public class MySQLConsistencyChecker{ public static final Logger LOGGER = LoggerFactory.getLogger(MySQLConsistencyChecker.class); private final MySQLDataSource source; private final ReentrantLock lock; private AtomicInteger jobCount = new AtomicInteger(); private String countSQL; private String maxSQL; private String tableName; // global table name private long beginTime; // private String columnExistSQL = "select count(*) as "+GlobalTableUtil.INNER_COLUMN // + " from information_schema.columns where column_name='" // + GlobalTableUtil.GLOBAL_TABLE_MYCAT_COLUMN + "' and table_name='"; // 此处用到了 mysql 多行转一行 group_concat 的用法,主要是为了简化对结果的处理 // 得到的结果类似于:id,name,_mycat_op_time private String columnExistSQL = "select group_concat(COLUMN_NAME separator ',') as " + GlobalTableUtil.INNER_COLUMN +" from information_schema.columns where TABLE_NAME='"; //user' and TABLE_SCHEMA='db1'; private List<SQLQueryResult<Map<String, String>>> list = new ArrayList<>(); public MySQLConsistencyChecker(MySQLDataSource source, String tableName) { this.source = source; this.lock = new ReentrantLock(false); this.tableName = tableName; this.countSQL = " select count(*) as "+GlobalTableUtil.COUNT_COLUMN+" from " + this.tableName; this.maxSQL = " select max("+GlobalTableUtil.GLOBAL_TABLE_MYCAT_COLUMN+") as "+ GlobalTableUtil.MAX_COLUMN+" from " + this.tableName; this.columnExistSQL += this.tableName +"' "; } public void checkRecordCout() { // ["db3","db2","db1"] this.jobCount.set(0); beginTime = new Date().getTime(); String[] physicalSchemas = source.getDbPool().getSchemas(); for(String dbName : physicalSchemas){ MySQLConsistencyHelper detector = new MySQLConsistencyHelper(this, null); OneRawSQLQueryResultHandler resultHandler = new OneRawSQLQueryResultHandler(new String[] {GlobalTableUtil.COUNT_COLUMN}, detector); SQLJob sqlJob = new SQLJob(this.getCountSQL(), dbName, resultHandler, source); detector.setSqlJob(sqlJob); sqlJob.run(); this.jobCount.incrementAndGet(); } } public void checkMaxTimeStamp() { // ["db3","db2","db1"] this.jobCount.set(0); beginTime = new Date().getTime(); String[] physicalSchemas = source.getDbPool().getSchemas(); for(String dbName : physicalSchemas){ MySQLConsistencyHelper detector = new MySQLConsistencyHelper(this, null); OneRawSQLQueryResultHandler resultHandler = new OneRawSQLQueryResultHandler(new String[] {GlobalTableUtil.MAX_COLUMN}, detector); SQLJob sqlJob = new SQLJob(this.getMaxSQL(), dbName, resultHandler, source); detector.setSqlJob(sqlJob); sqlJob.run(); this.jobCount.incrementAndGet(); } } /** * check inner column exist or not */ public void checkInnerColumnExist() { // ["db3","db2","db1"] this.jobCount.set(0); beginTime = new Date().getTime(); String[] physicalSchemas = source.getDbPool().getSchemas(); for(String dbName : physicalSchemas){ MySQLConsistencyHelper detector = new MySQLConsistencyHelper(this, null, 1); OneRawSQLQueryResultHandler resultHandler = new OneRawSQLQueryResultHandler(new String[] {GlobalTableUtil.INNER_COLUMN}, detector); String db = " and table_schema='" + dbName + "'"; SQLJob sqlJob = new SQLJob(this.columnExistSQL + db , dbName, resultHandler, source); detector.setSqlJob(sqlJob);//table_schema='db1' LOGGER.debug(sqlJob.toString()); sqlJob.run(); this.jobCount.incrementAndGet(); } } public void setResult(SQLQueryResult<Map<String, String>> result) { // LOGGER.debug("setResult::::::::::" + JSON.toJSONString(result)); lock.lock(); try{ this.jobCount.decrementAndGet(); if(result != null && result.isSuccess()){ result.setTableName(tableName); list.add(result); }else{ if(result != null && result.getResult() != null){ String sql = null; if(result.getResult().containsKey(GlobalTableUtil.COUNT_COLUMN)) sql = this.getCountSQL(); if(result.getResult().containsKey(GlobalTableUtil.MAX_COLUMN)) sql = this.getMaxSQL(); if(result.getResult().containsKey(GlobalTableUtil.INNER_COLUMN)) sql = this.getColumnExistSQL(); LOGGER.warn(sql+ " execute failed in db: " + result.getDataNode() + " during global table consistency check task."); } } if(this.jobCount.get() <= 0 || isTimeOut()){ GlobalTableUtil.finished(list); } }finally{ lock.unlock(); } } public boolean isTimeOut(){ long duration = new Date().getTime() - this.beginTime; return TimeUnit.MINUTES.convert(duration, TimeUnit.MILLISECONDS) > 1; // 1分钟超时 } public String getCountSQL() { return countSQL; } public String getColumnExistSQL() { return columnExistSQL; } public void setColumnExistSQL(String columnExistSQL) { this.columnExistSQL = columnExistSQL; } public String getMaxSQL() { return maxSQL; } public String getTableName() { return tableName; } public MySQLDataSource getSource() { return source; } }