[搜片神器]單服務器程序+數據庫流程優化記錄

DHT抓取程序開源地址:https://github.com/h31h31/H31DHTDEMOgit

數據處理程序開源地址:https://github.com/h31h31/H31DHTMgrgithub

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

以前介紹的文章因爲沒有大型網站的設計思路,本身如今感受有不少地方須要優化,特此記錄一下,也向你們學習下.

服務器出現的問題有:                                                                                   

1.當服務器查詢本地一個文件是否存在都須要200MS(毫秒)的時候的時候你怎麼辦?(文件夾有4096個一級文件夾,每一個文件夾有1000個文件,總大小在150G)

2.當服務器查詢一條記錄是否存在的時候須要500MS的時候你怎麼辦?(數據庫有350萬的數據紀錄)

3.當服務器網站搜索關鍵詞的時候須要5S左右的時候須要怎麼辦?(目前搜索採用SQL語句的LIKE查詢)

 問題1:先看下服務器移動文件中的花費時間:                                                                                sql

4096個文件夾須要移動剛開始估計34個小時(看下圖大概一分鐘處理2個文件夾,一小時處理120個,總共須要34小時),數據庫

看來當初的設計頗有問題,光移動就須要花費這麼久,之後數據再大點,設計若是須要修改還須要更多時間服務器

 

最開始設計的是使用2位數字作爲文件夾名字,最後發現一個文件夾下文件太多了,設計移動爲三位數字母爲文件夾名字,就到了目前這個狀態,發現查詢文件是否存在,架構

調用File.Exists()就須要花費200MS左右,查詢數據庫也須要花費500MS左右,到了須要考慮修改設計的問題了.ide

考慮迅雷服務器上若是下載種子的路徑,工具

http://bt.box.n0808.com/13/28/13ce77b3b934b12dc77fded6646426a6db5c3428.torrent學習

發現是取用HASH字符的前兩個字母爲一級文件夾,取HASH字符串的最後兩個字母爲二級文件夾,這樣就有256*256個文件夾,測試

比目前16*16*16=4096個文件夾多了不少,但這不是最關鍵的,查詢二級文件夾裏面的文件可能比一級文件夾快不少,目前猜想是這樣的,否則迅雷服務器也不會這樣設計.

目前服務器上已經跑了36小時尚未移動完,真是慢啊.

        private void MoveTorrentFileToSubDirThread()
        {
            string pathname = H31Data.m_saveTorrentPath;
            //先直接建立後256*256個文件夾
            for (int i = 0; i < 256; i++)
            {
                string temp1 = string.Format("{0}\\{1:X2}",pathname,i);
                if (!Directory.Exists(temp1))
                    Directory.CreateDirectory(temp1);
                for (int j = 0; j < 256; j++)
                {
                    string temp2 = string.Format("{0}\\{1:X2}", temp1, j);
                    if (!Directory.Exists(temp2))
                        Directory.CreateDirectory(temp2);
                }
            }
            DirectoryInfo filedir = new DirectoryInfo(pathname);
            foreach (DirectoryInfo NextFolder in filedir.GetDirectories())
            {
                if (NextFolder.Name.Length == 3)
                {
                    Int32 ticktime1 = System.Environment.TickCount;
                    int cnt = 0;
                    foreach (FileInfo fileChild in NextFolder.GetFiles("*.torrent"))
                    {
                        cnt++;
                        try
                        {
                            string fc = fileChild.ToString();
                            int finddot = fc.IndexOf('.');
                            string hashname = fc.Substring(0, finddot);
                            string tempname1 = hashname.Substring(0, 2).ToUpper();
                            string tempname2 = hashname.Substring(hashname.Length - 2, 2).ToUpper();
                            string pathname1 = Path.Combine(pathname, tempname1);
                            string pathname2 = Path.Combine(pathname1, tempname2);
                            string filename2 = Path.Combine(pathname2, fc);
                                File.Move(fileChild.FullName, filename2);
                        }
                        catch (Exception ex)
                        {
                            H31Debug.PrintLn(ex.Message);
                        }
                    }
                    try
                    {
                        Directory.Delete(NextFolder.FullName);
                    }
                    catch (System.Exception ex)
                    {
                        H31Debug.PrintLn(ex.Message);
                    }
                    Application.DoEvents();
                    Int32 ticktime2 = System.Environment.TickCount;
                    LogTheAction(10, 1, NextFolder.Name + "文件夾" + cnt.ToString() + "" + (ticktime2 - ticktime1).ToString() + "ms");
                    H31Debug.PrintLn(NextFolder.Name+"文件夾"+cnt.ToString()+""+(ticktime2-ticktime1).ToString()+"ms");

                }
                else if (NextFolder.Name.Length == 1)
                {
                    try
                    {
                        Directory.Delete(NextFolder.FullName);
                    }
                    catch (System.Exception ex)
                    {
                        H31Debug.PrintLn(ex.Message);
                    }
                }
            }
        }
移動種子文件到子文件夾下

 

 問題2:服務器上查詢花費時間長的問題:                                                                                            

因爲服務器上對40字節的HASH值爲惟一主鍵,以前數據不多時,只須要30MS左右,如今網站一塊兒運營的時候,查詢起來越慢,俗話說沒有很差的硬件,只有很差的軟件設計流程.

             

如何讓程序少用數據庫,從而讓網站儘可能使用數據庫目前須要考慮的問題的.

因爲之前的項目中使用過數據庫批量插入文本數據的經驗,目前考慮拿出來使用,減小數據庫的壓力.

 

使用bulk insert批量插入數據,必須考慮每個字段都必須對應上,

 沒有的能夠空着,但必須有分隔符.好比下圖的第一例就沒有數據,由於對應表字段是自增的字段,沒有辦法填寫,空着就行.

   

下面看下如何將文本數據插入數據庫中,FIELDTERMINATOR是分隔符,ROWTERMINATOR是換行符,其它參數本身搜索下會更詳細.

 

        /// <summary>
        /// 批量插入
        /// </summary>
        public static int BulkInsertFile(string tablename,string filepath)
        {
            try
            {
                string strSql = string.Format("BULK INSERT {0} FROM '{1}' WITH ( FIELDTERMINATOR='|',ROWTERMINATOR=';\\n',BATCHSIZE = 5000)", tablename, filepath);
                return dbsql.ExecuteNonQuery(CommandType.Text, strSql.ToString(), null);
            }
            catch (System.Exception ex)
            {
                H31Debug.PrintLn("AddNewHashLOGFile" + filepath + ex.StackTrace);
            }

            return -1;
        }

須要注意的問題有如何保證中文進數據庫不是亂碼的問題.

保存的時候使用Unicode編碼.SQLSERVER2005之後就不支持UTF8就好了.有些日文等保存到數據庫就會有問題,採用Unicode編碼就行了.

                StreamWriter writer = new StreamWriter(filename, true,System.Text.Encoding.Unicode);
                writer.WriteLine(content);
                writer.Close();

 使用BULKINSERT批量插入就須要將HASH表裏面的ID字段取消自增的屬性,在本地操做基本上沒有什麼問題,由於本地數據量小,但一到服務器上就不行了,

12個表有一個表數據量大,修改不了,其它表修改爲功,嚇得直接冒汗了,

若是修改不了,批量插入的設計就是白白設計的,由於HASH表的ID字段被關聯到其它表了,因此本地生成文件的時候這個ID必須有.                                                                                                               

數據庫去掉ID自增屬性提示錯誤:                                                                                                                                            

Microsoft SQL Server Management Studio
---------------------------
Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.

 網上找了一堆的方法,說是修改工具欄裏面的選項,以下圖所示:

發現修改後仍是沒有用,數據庫重啓也沒有用.

急得不行的狀況下,掛停網站,將數據庫分離收縮附加回來再修改,仍是執行超時.

在沒有人請教的狀況下,只能慢慢的GOOGLE搜索英文的文章了,終於發現一個修改註冊表的方法了.

This timeout setting value is configurable through the DWORD value SQLQueryTimeout at HKEY_CURRENT_USER\Software\Microsoft 
\Microsoft SQL Server\90\Tools\Shell\DataProject.
modifying SQLQueryTimeout to 300, but it still times out in 30 seconds. 

剛開始修改成300秒也不行,直接修改成1800秒,也基本上無效,只能重啓數據庫,多點幾回保存修改,後來有一次成功了.看來工具也須要有會配置的時候..

 

軟件流程從新設計:                                                                                                                 
因爲以前讓數據庫自動生成自增ID,目前看來是個錯誤的設計,如今只能在錯一半基本上改了,由於從新跑一次全部數據,估計最少須要15天的時間.

如今儘可能不與數據庫實時打交道,程序啓動時,遍歷表的最大ID號,而後本地用來生成自增ID號直接存儲數據到本地文件文件中,

就須要保證ID號自增的惟一性了,哪就須要考慮:

1.軟件惟一運行的問題;

2.軟件開始運行後保證之前生成的SQL批量文本文件所有插入到數據庫中,而後才能去取最大ID號的問題;

3.生成的SQL批量文本文件須要考慮若是插入不成功就可能數據庫已經存在的問題;

4.存在的狀況下須要讀取一條條插入的問題.

目前基本上軟件的流程修改得差很少了,少用數據庫,這就給網站查詢速度更小的壓力了.

 

問題3:網站查詢速度很慢的問題                                                                                             

因爲網站仍是採用SQL的LIKE語句來搜索,因此時間大概在2-5S的時間,特別是搜索結果比較多的時候顯示更慢.

通過你們的推薦使用hubble.net,Lucene.net目前分析的結果是採用Lucene.net.

因爲服務器目前內存不夠的狀況下,只能讓CPU用起來的問題了,Lucene採用文原本索引,佔用內存少的狀況下,目前只能這麼架構測試了.

因爲Lucene還在研究中,因此後期有什麼不會的地方,請你們指點下.

總結:                                                                                                     

1.目前尚未搞明白移動不少小文件須要這麼長的時間.

2.批量插入會不會引發什麼其它的問題還須要進一步觀察.

但願有了解的朋友在此留言指教下.

但願你們多多推薦哦...你們的推薦纔是下一篇介紹的動力...

 

祝你們國慶節快樂.........

相關文章
相關標籤/搜索