在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 掉 數據庫就好了。