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