第一次操刀數據庫分表的教訓與經驗


給數據庫分表,曾經是個離我很遙遠的事情,入行這幾年參與過別人主導的分表、分庫工作,自己從未操刀,直到我自己的垃圾小站主表10多萬記錄,相關的其他表30~40萬,孱弱的服務器面對谷歌和百度的抓取,導致下載一個頁面的時間非常長(PS:谷歌的蜘蛛是我小站的主要用戶)。

image

因為我抓取的主要是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,結果還沒開工內存就沒了咬牙切齒

image

2谷歌抓取顯示等待時間

image

到底是什么問題導致了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一個對象貌似不靠譜,於是我小改一下,本地測試正常讀取數據沒問題,讓谷歌蜘蛛去做並發測試吧吐舌鬼臉

上線后,內存就下來了!至於速度有沒有提升,需要等幾天谷歌抓取統計信息才可以看出來

image

內存又有了,lucene可以走起了!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM