給數據庫分表,曾經是個離我很遙遠的事情,入行這幾年參與過別人主導的分表、分庫工作,自己從未操刀,直到我自己的垃圾小站主表10多萬記錄,相關的其他表30~40萬,孱弱的服務器面對谷歌和百度的抓取,導致下載一個頁面的時間非常長(PS:谷歌的蜘蛛是我小站的主要用戶)。
因為我抓取的主要是stackoverflow、微軟社區、雅虎問答等和計算機相關的數據庫,存儲比較簡單,分這么幾個表
1:url表,記錄每個問題原始的url地址 10萬
2:問題內容表,記錄問題的主要內容 10萬
3:答案表;記錄問題的答案 20萬
4:用戶信息表 20萬
5:問題和tag的關系表 30萬
我存儲的時候比較簡單,用問題的title 的哈希值作為唯一值,防止問題重復
以前看過一些文章,關於數據庫分表分庫的主要是數據的均勻和可擴展性方面的考慮,我考慮常見的幾種方式
1:按照時間分表----因為我的采集程序是我想開就開,想關就關,沒有時間規律,所以不能這么干
2:按照分類分表----我主要有兩個分類1是網站分類,二是tag分類,網站的分類的話stackoverflow的數據占了70%,tag分類的話也不是很均勻,因為我是按照關鍵詞抓取信息的
3:一個障礙,我現在的網站url里面沒有分類和時間信息,如果按照分類和時間分表,根據url信息取不到數據了
4:一個僥幸,好在我沒把自增id作為url一部分,僅僅是作為優化數據庫的標配(記不清在那里看過,有主鍵的表比沒主鍵的表好,有自增id比沒自增id的好)
最后我決定根據我的title的哈希值分表,因為url地址里面包含了這個東西,根據哈希值就可以找到這個表
磨刀霍霍分表,一個表是拆10個表?100個表?1000表?,我是根據哈希值的后三位分表 例如后三位是123 表就是table_123,最后為了防止出現數據不均勻,我一個表拆分1000張,雖然然該集中的還在集中,但是分完表后,一個表要達到10萬數據應該是猴年馬月的事情了
我的數據庫是mysql的,園子里的一個牛人的分表工具很好使
http://www.cnblogs.com/hb_cattle/archive/2011/10/12/2208910.html 這是地址,最重要的是,這是我在園子里面下載的代碼里面唯一有單元測試的代碼,用過之后,發現有單元測試的東西質量果然有保證,堅定了我學習單元測試的決心
於是我三下五除二分了8000張表出來,這個時候遇到了郁悶的問題
如何快速有效的吧數據從原來的表里取出來,在放到我新的表里面去,我寫了存儲過程效果奇差,分一個表要20~40分鍾,這在正式生產環境是絕對不允許的,我的代碼如下,
DELIMITER $$ USE `learn`$$ DROP PROCEDURE IF EXISTS `genQu`$$ CREATE DEFINER=`root`@`localhost` PROCEDURE `genQu`() BEGIN DECLARE i INT DEFAULT 0; DECLARE l_sql VARCHAR(4000); WHILE i <1000 DO SET l_sql=CONCAT("INSERT INTO question_",i," SELECT * FROM question WHERE RIGHT(question.titlecode,3)=",i); SET @SQL=l_sql; SELECT @SQL; PREPARE s1 FROM @SQL; EXECUTE s1; DEALLOCATE PREPARE s1; SET i = i + 1; END WHILE; END$$ DELIMITER ;
因為我去數據的查詢語句走不了索引,所以導致我去數據異常緩慢,這算是這次分表的一個教訓
教訓1:分表時候要考慮取數據和插入數據的性能問題
雖然慢,但是還可以接受,每天晚上弄一個表,1周就把所有的表弄完了
分完表后,我開始改程序,抓取程序和web站點讀寫數據的部分,我查了一下,
我抓取數據用的orm http://viciproject.com/wiki/projects/mvc/home 徹底不支持分表和分庫
我做web站點用的orm https://github.com/toptensoftware/PetaPoco 支持的不是很友好
於是我用園子里面的orm,結果悲劇的一幕開始了,面對一個庫8000張表,園子里面4大orm的模型生成工具都不能很好的工作,
教訓2:分表前,先要選好orm工具和生成實體類
雖然PetaPoco 不是很友好的支持分表,但是,他簡單代碼少,很容易就找到了支持分表的方法。
經驗3:工具選的越簡單越單一,遇到麻煩好處理
於是我開始修改代碼,一周后,網站和抓取程序上線,我原以為谷歌抓等待的時間會下降,服務器的cpu和內存和下降,結果悲劇的一幕出現了。
1 監控顯示內存急速上升,1G內存就剩下100M了,郁悶的我計划學習lucene,結果還沒開工內存就沒了
2谷歌抓取顯示等待時間
到底是什么問題導致了mysql和iis內存上升了( mysql200M、iis300M),悲劇的是我以前沒留意mysql和iis分別用多少內存,一時半會不知道2個東西那個出了問題
教訓4:服務器性能監控數據很重要,發現問題可以快速定位
到底是那里除了問題?本着我寫的程序無bug的態度,必然是mysql的問題,因為分表的sql語句太簡單了,mysql處理8張表和8000張表的配置應該不一樣吧。。。。。。。
經過亂起八糟的查找,mysql有一個配置
SHOW GLOBAL STATUS LIKE 'open%tables%'
好像是配置打開文件的數量等相關的配置還有一些,可見mysql里面放8000張表和8張表在配置上的確有些區別,因為不是很懂mysql,所以就不研究了,
教訓5:分表前要對數據庫做基本的隨機讀寫壓力測試,測試適合的配置。。。。。。。。。。
應該是我的程序寫的有問題
程序有問題改咋整呢,和上次的程序除了分表的sql貌似沒啥區別,第一次裝vs2012,看看vs2012的內存采樣能不能猜出問題
可能因為我是手工點擊的,沒能模仿谷歌爬蟲,vs2012的內存采樣沒有猜出問題
教訓6:性能檢測不能過分依賴工具
程序到底是那里有問題呢?
這次比上個版本讀取數據庫唯一的區別是我直接調用了一個orm不鼓勵調用的方法,主要是不這么干,就沒法支持分表了
learnDAL.learnDB.GetInstance()
public static IFactory Factory { get; set; } public static learnDB GetInstance() { if (_instance != null) return _instance; if (Factory != null) return Factory.GetInstance(); else { //備注以前是 return new learnDB() _instance = new learnDB(); return _instance; } }
於是我調試了一下,跟進去,雖然沒有明顯的錯誤,但是感覺每次new一個對象貌似不靠譜,於是我小改一下,本地測試正常讀取數據沒問題,讓谷歌蜘蛛去做並發測試吧
上線后,內存就下來了!至於速度有沒有提升,需要等幾天谷歌抓取統計信息才可以看出來
內存又有了,lucene可以走起了!