第一次操刀数据库分表的教训与经验

给数据库分表,曾经是个离我很遥远的事情,入行这几年参与过别人主导的分表、分库工作,自己从未操刀,直到我自己的垃圾小站主表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可以走起了!

posted @ 2013-11-08 21:50  互联网Fans  阅读(5641)  评论(4编辑  收藏  举报