mysql删除数据库的过程

在dba圈混迹很久了,早早就想写点博客,总结一些经验什么的,总是因为时间少呀,人懒呀,觉得文笔不好呀啥杂七杂八原因,一直很少写点东西,

不过前几天和一朋友讨论了一下删库的过程,我觉得挺有意思,

准备记录一下。

 

有删库需求情况挺多,比如有一天项目结束了需要,开发说要删除这个数据库。也许机器磁盘不够了,上面正好有一个很大的库,但是好像又没用,删除可以释放资源,比如某一天你突然觉得这个没有的库放在那里肯碍眼,想删除它,

当然也有可能是业务需求,会经常创建和删除库 等等情况, 其实说明 删库这个需求确认真实存在的。

 

也许你会觉得删库这个问题很简单,一个命令而已,  drop database db_name; 是不是很简单。

 

我们删除属于DDL操作,不能回滚数据,如果需要恢复,那基本需要全量备份+binlog,

影响恢复时间的因素很多: 恢复决策的时间,dba恢复数据熟练度,实例大小,数据修改频繁度,以及机器配置,网络传输速度 和其他因素

而且不管怎么说,如果删除的数据库,里面还有业务访问,影响业务是肯定,不能快速恢复也是很蛋疼的事儿。

所以我需要做的就是,尽量减少删除后需要恢复的可能性。

我们需要考虑的情况比较多,

下面列举一部分常见,其他根据不同场景大家可以自己考虑考虑,也是一个总结的过程哇

一、开发或者业务提出要删库:

1、开发提出需要删除 某某库,db应该需要确认该库是否可以删除

     a)、如果程序使用了长连接,我们可以先看看是否有该库的连接,如果有该库的连接,直接驳回,让开发确认程序是否下线完成。可以使用下面语句确认。

    select * from information_schema.processlist where DB='$db_name';

     b)、如果检查未发现长连接,则需要查看是否有短连接的程序访问:如果有访问依旧需要找开发确认,如果没有访问,有条件可以多观察一段时间。

   可以借助简单的工具 mysql-sniffer :

        ./mysql-sniffer -i bond0 -p 6006  |grep "$db_name"   ,可以获取到访问ip,库名,sql语句等,通过grep工具过滤可以得到很小的结果集,使用非常方便。

         项目为360开源项目:https://github.com/Qihoo360/mysql-sniffer,当然也可以使用tcpdump,开启general_log 的方式来做。办法有很多种

    c)、已经确认确实程序已经没有使用程序访问该库,我们就需要开了删库了,

          方案1、先remoke 该库相关的权限,然后观察一段时间,如果没有问题就可以删库。

                     优点:就是以后建立同名库,不存在权限问题,

                     缺点:如果考虑操作回滚,需要备份数据库的权限,最好还只能备份被remoke的权限,这个过程其实不好做,考虑的因素挺多。

          方案2、不remoke 权限,直接rename 数据库名,原理是先建立一个新库,将需要重命名的库里面的表全部通过RENAME TABLE tbl_name TO new_tbl_name的方式将表放入新的库里面,

                     然后将老库删除。需要注意,新老库要放在同一个磁盘分区里面,不然效率会特别低,这个过程使整个实例hang住,后果很严重。

                    优点:不用做任何单独备份任何东西,回滚非常方便。

                    缺点:考虑同名库权限问题,还是需要回收相关的权限,由于我们没有同名数据库的问题,所以权限并未回收。

       个人倾向方案2,简单好实施,并且有简单的脚本:

#!/bin/sh
function executeSql()
{
    sql="$1"

    if [ "$sql" = "" ]
    then
        cat | mysql --default-character-set=utf8 -N 
    else
        echo "$sql" | mysql --default-character-set=utf8 -N 
    fi
}



function mysqlDBRename()
{
        
        tempFromDBName="${fromDBName}"
        tempToDBName="${toDBName}"
        #建库
        echo "create database ${toDBName};" | executeSql
        if [ "$?" -ne 0 ]; then
                echo "Error: Some error occur when create database ${toDBName}."
                exit 0
        fi

        echo "select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA='$fromDBName' and TABLE_TYPE='BASE TABLE';" | executeSql | while read tableName; do
                echo "alter table ${fromDBName}.${tableName} rename ${toDBName}.${tableName};" | executeSql
                if [ "$?" -ne 0 ]; then
                        echo "Error: Some error occur when execute 'alter table ${fromDBName}.${tableName} rename ${toDBName}.${tableName};'"   
                        exit 1
                fi
        done
        if [ "$?" -ne 0 ]; then
                exit 0
        fi
    
        echo "drop database ${fromDBName}" | executeSql
        if [ "$?" -ne 0 ]; then
               echo "Error: Some error occur when drop database ${fromDBName}."
               exit 1
        fi
        break

}


function main()
{
        fromDBName="$1"
        toDBName="$2"
        mysqlDBRename
}
main "$1" "$2" 

 

  d)、删库;

         当步骤c做了一段时间,比如1周,确认程序已经完全下了后。

   1、如果该实例上面没有其他的实例,则可以直接将实例shutdown,删除实例,将最后一个备份文件归档,整个过程就算完成

           2、如果该实例还有其他业务正在使用,则还需要考虑更多问题:

                 需要删除是库是否很大,是否有很多数量的表,比如超过1000张表,如果磁盘性能不够好,可能会对线上查询元数据的操作阻塞,例如:使用了存储过程,那么都会阻塞在读取存储过程这个步骤。

                 因此建议 1、分批次删除表,

         2、如果有大量分区的分区表,建议先删除分区。

                               我这有一个存储过程可以做这个事儿,前提是需要将dbman rename成 del_xxxx这种格式的。在需要删除分区的库里面创建该存储过程,然后调用存储过程就可以。

DELIMITER $$

DROP PROCEDURE IF EXISTS `droppartion_del`$$

CREATE  PROCEDURE `droppartion_del`()
    SQL SECURITY INVOKER
label1:BEGIN
declare _droppart int;
declare _i int;
DECLARE _dbname varchar(40);
declare _droppartlist varchar(500);
set _dbname=database();
        if (select count(*) from information_schema.PROCESSLIST where db =_dbname) >1 then
             select -1,'some process on db, please check it !';
            LEAVE label1;
        end if ;
    if   substring_index(_dbname,'_',1)='del'  then         
        drop table if exists tablename_tmp;
        create table tablename_tmp(`id` int(11) unsigned NOT NULL AUTO_INCREMENT,table_name varchar(40),primary key (id));
        insert into tablename_tmp(table_name) select table_name from information_schema.PARTITIONS where table_schema =_dbname and partition_name is not null group by  table_name HAVING count(*) >3;
        select count(*) into @tablenum from tablename_tmp;
        set _i=1;  
        while _i <=@tablenum  do 
            select substring(partition_name,2) into _droppart  from information_schema.PARTITIONS  where table_schema = _dbname and table_name =(select table_name from tablename_tmp where id=_i ) order by partition_name  desc limit 2,1;
            select group_concat(partition_name) into _droppartlist from information_schema.PARTITIONS where table_schema =_dbname and table_name=(select table_name from tablename_tmp where id=_i ) and substring(partition_name,2) <_droppart  group by table_schema,table_name;
            SET @stmt = CONCAT('alter table ' ,(select table_name from tablename_tmp where id=_i),' drop partition ' ,_droppartlist,' ;');
            prepare SQL1 FROM @stmt;
            EXECUTE  SQL1;        
            set _i=_i+1;
        end while;
        
        truncate table tablename_tmp;
        insert into tablename_tmp(table_name) select table_name from information_schema.TABLES where table_schema =_dbname and table_name<>'tablename_tmp';
        select count(*) into @tablenum from tablename_tmp;
        
        set _i=1;  
        while _i <=@tablenum  do 
            SET @stmt = CONCAT('drop table if exists ' ,(select table_name from tablename_tmp where id=_i),' ;');
            prepare SQL1 FROM @stmt;
            EXECUTE  SQL1;        
            set _i=_i+1;
        end while;
        
        select 1,'succeed!!';
    else 
        select -2,'we can only drop database which name like  del_XXX,please check it!';
        LEAVE label1;
    end if;
        
END$$

DELIMITER ;

 

 

二、DBA认为某些库需要删除:

        这种情况,一般是空间不足,然后有dba 认为不需要使用的库,可以清理的时候。

       流程基本和上面一种情况差不多,只是要dba主动找开发,业务确认,并且做好个方便的准备来确认库是否还有业务访问。

三、业务逻辑需要按规则删除库:

  对于这个需求,主要是游戏业务,合区过后清除数据的时候使用,这种相对来说判断就要简单一些,只是不确定每个表的大小,表的数量,建议在做整合脚本的时候,尽量化整为零,多次删除。

 

 

总结,以上情况都是基于线上环境考虑的, 如果仅仅是开发测试环境,个人觉得没有相对没那么麻烦,

做了基本确认, 然后做rename database, 备份,一段时间drop 掉 数据库就好了。

 

posted on 2017-07-12 14:53  wenlongy  阅读(2914)  评论(0编辑  收藏  举报