- CREATE TABLE `contact784` (
- `cid` bigint AUTO_INCREMENT NOT NULL,
- `uid` bigint NOT NULL,
- `email` varchar(128) NOT NULL,
- `name` varchar(64) NOT NULL,
- `mobile` varchar(16) NULL,
- `atime` timestamp NULL,
- `type` enum('BLACK','WHITE','NORMAL') NOT NULl default 'NORMAL',
- `info` text NULL,
- `memo` varchar(1024) NULL,
- PRIMARY key(`cid`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT = 100;
- ALTER TABLE `contact784` ADD UNIQUE INDEX uniq_uid_email(`uid`,`email`);
step2,插入了100W數據:
- # -*- coding: utf-8 -*-
- #@author python.han@gmail.com
- import MySQLdb
- import random
- import string
- import threading
- import time
- domains = ['org','com.cn','qq.com','yahoo.com','163.com','com','cn','sina.cn','sina.com']
- host = "localhost"
- user = "xx"
- pwd = "xx"
- db = "t3"
- def getRandomValue():
- email = ""
- s = ""
- for x in range(random.randint(1,10)):
- s += random.choice(string.letters)
- b = list(s)
- domain = ''.join(b)+"."+random.choice(domains)
- email = s+"@"+domain
- return email,s
- def insert(count):
- conn=MySQLdb.connect(host=host,user=user,passwd=pwd,db=db)
- cursor=conn.cursor()
- for cid in xrange(count):
- uid = random.randint(1000000000,9999999999)
- email,name = getRandomValue()
- sql = "insert into contact784(uid,email,name) values (%d,'%s', '%s')" %(uid,email,name)
- n=cursor.execute(sql)
- cursor.close()
- conn.commit ()
- conn.close()
- if __name__=='__main__':
- start = time.clock()
- for i in range(100):
- worker = threading.Thread(target = insert(10000))
- worker.start()
- end = time.clock()
- print "elsaped:%s" %(end-start)
step3,要重新單線程插入,需要把數據清空.
因為python多線程由於GIL的關系,實際上上面的100個線程只產生了一個連接,需要測試一下純單線程插入是不是要快些:)
執行:delete from contact784
半小時沒有執行完畢!
診斷方式:
1,iostat ,top等查看磁盤io很大
2,inotifywatch發現io的事件非常多
原因:在大表上使用delete from 清空一個表是非常慢的。因為InnoDB必須處理表中的每一行,根據InnoDB的事務設計原則,首先需要把“刪除動作”寫入“事務日志”,然后寫入實際的表。所以,清空大表的時候,最好直接drop table然后重建。
注:
在delete from 執行的過程中:
用:select count(*) from contact784;發現表的數據量一直是100行
用:explain select count(*) from contact784;可以發現數量一直在減少,顯示當前
784是是因為前面這個文章的原因“
http://hanyh.iteye.com/blog/431323
delete是個極其昂貴的操作哦,它會產生大量的undo數據(最多的),你每刪一次oracle都要記錄一次。
如果從undo角度來看的話,可以優化的就是控制事務的長度,即用:commit。
一次delete速度異常慢的處理過程
轉自:http://space.itpub.net/10710960/viewspace-610982
一次小數據量刪除,但花費2個小時還沒完成的問題
delete from TOPBOX_COURSEWARE where id like '760%';
花費非常長的時間,topbox_courseware表大概2w數據,要刪除的也就2500條數據。
問題原因:
由於TOPBOX_COURSEWARE表與多個表有外鍵關聯,而且關聯的表中有2張千萬級別的大表。
通過v$session_wait,v$session表
select * from v$session_wait a,v$session b
where b.sid=a.sid
and a.event not like 'SQL*Net%';
發現該session的event是db file scattered read。
這個事件一般是表示法傷了全表掃描相關的等待。通常意味着全表掃描過多,或者I/O能力不足,或是I/O爭用造成的。
解決方法:
1.通過dba_constraints表找到topbox_courseware表對應的約束
select * from dba_constraints where constraint_type='R' and wner='TOPBOX' and r_constraint_name='PK_TOPBOX_COURSEWARE'
得到兩個外鍵約束名
FK_TOPBOX_COURSCO_REF_COUR
FK_TOPBOX_CSTUDY_REF_COUSE
2.通過命令將這2個約束disable
alter table topbox_coursescore disable constraint fk_topbox_coursco_ref_cour;
alter table topbox_coursestudy disable constraint fk_topbox_cstudy_ref_couse;
通過上面的處理delete只需要不到1秒的時間
3.將約束重新激活由於topbox_coursescore,topbox_coursestudy是千萬級的大表,如果直接enable而不加其他參數,啟用約束后, oracle會對表中數據
逐條檢查,所以速度會非常慢。而且已經插入的數據沒有臟數據,所以為了避免不必要的工作,就要使用novalidate
alter table topbox_coursescore enable novalidate constraint fk_topbox_coursco_ref_cour;
alter table topbox_coursestudy enable novalidate constraint fk_topbox_cstudy_ref_couse;