天天看點

MySQL删除資料的簡單嘗試

在Oracle裡面對于資料清理,如果是非分區表,目前我經常的處理思路是下面三個。

第一種是中規中矩,做好備份,然後開始清理,當然這種情況隻是說明資料清理的部分,不考慮高水位線的影響。可以使用shrink,move tablespace等來處理。

補充一句,其實這個dump這是一種形式,可以采用各種形式的資料導出格式,比如sqlldr适用的csv,比如外部表,比如expdp,exp的導出二進制dump等。

MySQL删除資料的簡單嘗試

第二種思路是邏輯備份,就是把表采用ctas的方式備份一份。然後對原來的表進行資料清理。這種情況下,占用的是資料庫内的資料空間。

MySQL删除資料的簡單嘗試

第三種思路是迂回戰術,就是把原表改一個别名,然後建立一個同名的表(表裡沒有資料,隻有表結構),然後把需要的增量資料插入到新表中.

MySQL删除資料的簡單嘗試

這種思路在MySQL裡面也是類似,不過值得一提的是MySQL的rename着實比較牛,因為MySQL中的database和Oracle中的user的含義有些類似,MySQL裡面很輕松的使用rename操作把一個資料庫A中的表TEST很輕松的rename倒資料庫B裡面。

最近開發的同僚回報有一個業務的查詢着實太慢,結果分析下來發現一種改善思路就是删除舊資料。因為确實很長時間沒有清理了。

簡單和開發溝通了一下,其實有幾種思路可以走,不過就看具體的需求了。開發說保留近半年的資料,提供的清理sql如下。

半年以前的資料有大概300萬。

mysql> select count(*)from recharge where occur_time<'2015-07-01 00:00:00';

+----------+

| count(*) |

|  2945974 |

1 row in set (1 min 20.13 sec)

需要保留的資料有50多萬。

mysql> select count(*)from fact_recharge where occur_time > '2015-07-01 00:00:00';

|   550422 |

1 row in set (1 min 25.46 sec)

是以按照這個比例,其實選用第三種方法看起來要好些,不過限于本地的空間,而且開發說這個表删除的舊資料需要檢視,恢複的可能性極小,是以我就一次弄幹淨點,直接實體備份出來清理,采用了第一種方式。

簡單評估之後就開始操作。

先開始做備份。

mysqldump --default-character-set=UTF8 --single-transaction -q -R --triggers --tables test_ad xxxx_regok  |gzip > /data2/dba/databak/tab_bak/full_20150203_us_test_ad_xxxx_regok.sql.gz

然後就按照正常思路開始删除,不過看起來很簡單的删除竟然還報錯了。

mysql> delete from recharge where occur_time<'2015-07-01 00:00:00';

ERROR 1197 (HY000): Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again

這個錯誤看來和binlog的cache size有很大的關系,目前的binlog設定如下

mysql> show variables like '%binlog%';

+-----------------------------------------+----------------------+

| Variable_name                           | Value                |

| binlog_cache_size                       | 4194304              |

| binlog_direct_non_transactional_updates | OFF                  |

| binlog_format                           | ROW                  |

| binlog_stmt_cache_size                  | 32768                |

| innodb_locks_unsafe_for_binlog          | OFF                  |

| max_binlog_cache_size                   | 536870912            |

| max_binlog_size                         | 1073741824           |

| max_binlog_stmt_cache_size              | 18446744073709547520 |

| sync_binlog                             | 0                    |

9 rows in set (0.00 sec)

而且比較糾結的是這個環境是采用了級聯複制,動一處需要關聯修改多處。目前的binlog cache size是500M左右。删除的資料肯定要大于這個cache_size.

是以這個時候還得使用另外一種迂回戰術,那就是分批删了。可以考慮使用datediff來作為一個基準删除。

現在距離2015年7月1日有217天的時間差,那麼我們就按照這個時間差來做點文章,分批删除。

mysql> select datediff(now(),'2015-07-01 00:00:00') ;

+---------------------------------------+

| datediff(now(),'2015-07-01 00:00:00') |

|                                   217 |

1 row in set (0.00 sec)

目前時間為:

mysql> select now();

+---------------------+

| now()               |

| 2016-02-03 00:01:28 |

當然老是喜歡用oracle的語句檢驗一下。

SQL> SQL> select sysdate-217 from dual;

SYSDATE-217

-------------------

2015-07-01 16:02:03

好了,開始删除資料,可以使用下面的語句,不過還需要改進一下。

delete from fact_recharge where datediff(now(),occur_time) >217

那麼删除的邊界值怎麼确定呢。

mysql> select max(datediff(now(),occur_time)) from fact_recharge where datediff(now(),occur_time) >217 ;

+---------------------------------+

| max(datediff(now(),occur_time)) |

|                           16835 |

1 row in set (3.69 sec)

這個結果讓我有些無語,應該是裡面有一些資料不光舊,而且還有問題。

SQL>select sysdate-16835 from dual

SYSDATE-16835

1969-12-31 16:04:59

需要調節删除的跨度。

mysql> delete from recharge where datediff(now(),occur_time)>218 and  datediff(now(),occur_time) < 800;

mysql> delete from recharge where datediff(now(),occur_time)>218 and  datediff(now(),occur_time) < 300;

Query OK, 310067 rows affected (36.78 sec)

mysql> delete from recharge where datediff(now(),occur_time)>300 and  datediff(now(),occur_time) < 500;

Query OK, 1065870 rows affected (1 min 50.08 sec)

mysql> delete from recharge where datediff(now(),occur_time)>500 and  datediff(now(),occur_time) <700;

Query OK, 1021640 rows affected (1 min 59.31 sec)

mysql> delete from recharge where datediff(now(),occur_time)>700 and datediff(now(),occur_time) < 1000;

Query OK, 505048 rows affected (2 min 29.91 sec)

資料已經大體删除,我們可以使用修改存儲引擎達到釋放碎片的目的了。

mysql> alter table recharge  engine=InnoDB;

Query OK, 594253 rows affected (4 min 19.94 sec)

Records: 594253  Duplicates: 0  Warnings: 0

修改之後,删除了大概2G左右的空間。

# ll recharge*|du -sh .

33G     .

31G     .

當然剛剛的删除還做了一些保留,為了對比,再次嘗試,删除的工作就很快了。

mysql> delete from recharge where datediff(now(),occur_time)>1000;

Query OK, 25712 rows affected (2.03 sec)

mysql> delete from recharge where datediff(now(),occur_time)>218;

Query OK, 14400 rows affected (1.05 sec)

是以通過這個小的嘗試也可以看出來其實有些處理思路還是相通的,但是技術細節上還有很多需要繼續琢磨的地方。