[搜片神器]单服务器程序+数据库流程优化记录

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.批量插入会不会引发什么其它的问题还须要进一步观察.

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

但愿你们多多推荐哦...你们的推荐才是下一篇介绍的动力...

 

祝你们国庆节快乐.........

相关文章
相关标签/搜索