日期:2014-05-16  浏览次数:20522 次

小小的重构,大大的认识
    作为开发人员,一直把重点关注在技术架构、代码、并发效率上,却从来没有认真仔细的虑数据库,昨天、今天做了一次小小的重构,顺便学习了一下MySql,发现这里大有搞头。小记一下这个过程,便于今后查阅。
    这是一个搜索系统,搜索引擎这边直接将数据库数据增量索引到索引库,提供给公司内部其他系统进行搜索,到没有出现性能问题。但是在搜索管理系统中,和数据库打交道比较多,中央-驻地二级管理运行模式,使得跨库事务、业务逻辑相对复杂一些,也出现一些较长的延迟等待。当初数据库是直接根据对象关系自动生成,由于开发紧迫,没有深究。
    首先,当初对象关系 Info 和 Keyword做双向为one-to-many,即:
    info中 <set name="keywords" cascade="all">
              <key column="info_id" not-null="true" />
                <one-to-many class="Keyword"/>
            </set>
    在keyword中<many-to-one name="info"  column="info_id" insert="false" update="false" class="Info" />
    其次,在Info的类型、子类型、目标类型等都作为many-to-one维护。但是,有一个垂直分类p_type仅仅作为Info的一个String维护着。
    然而,业务部门却非常关注这个p_type,最近上一个关键字统计模块,主要功能:
1) 根据p_type分类统计每一种p_type的节目数(info作为model的基类)、keyword数目,tag数
2) 统计总的节目数、keyword数目,tag数
3) 针对具体某一种p_type列出其下面得各keyword,以及该keyword出现频率
4) 两两比较不同p_type之间相同的keyword数,以及这些具体的keyword
    不算太复杂,但是纯用hibernate HQL肯定无法做的很好了,所以这边我直接用SQL,却发现数据库其实连3范式都算不上,这里只记一下keyword和info。
     info表大概21W行,keyword大概48W行,关系就维护在keyword中列(info_id),select distinct一下,发现keyword实际不同的关键字只有9W行,这多出来39W冗余数据啊;
     p_type作为varchar字段,在info中没有索引。
     于是进行了一下小小的改动:
1)新建temp_key(id,keyword),从keyword中select distinct插入到temp_key
2)相比keyword,temp_key量小,故为其keyword列临时建一个索引,便于后续操作更快 
3)新建数据库 info_key来维护info和keyword的关系
  ps:这里表类型需InnoDB,myisam不支持后面建外键操作
4) 删除keyword中重复记录的信息,原先的库结构会使得同一个info可能有相同的keyword记录,这是不必要的冗余,且会使得第六步info_key关系的插入受到阻扰。然而用语句:delete from keyword where id in (select id from keyword group by info_id,keyword having count(*)>=2),会有错误,原因是MySql中的行锁,在后半段select中锁定了keyword的行,在前半段却要做删除,当然不行,所以得用个临时表。
5)临时表:
create table temp_table(id bigint);
  insert into temp_table (id) (select id from keyword group by info_id,keyword having count(*)>=2)
删除:
delete from keyword where id in(select id from temp_table)
6)插入info,keyword的关联信息,info_key
insert into info_key (info_id,key_id) (select k.info_id,t.id from keyword k inner join temp_key t on k.keyword = t.keyword)
7)删掉keyword表,rename表temp_key为keyword
8)为info_key增加外键信息,当然是分别restrict到info和keyord的id
9)新建p_type(id,p_type),InnoDB
10)insert into p_type(p_type) (select distinct p_type from info)
11)为info新增列p_type_id bigint
12) update info set p_type_id = (select id from p_type where info.p_type = p_type.p_type);
13) 删除info中p_type
14)为info的p_type增加外键信息,关联到p_type.id
     经过以上小手术,关键字统计模块的性能比原先确实提高不少,最长的交叉比较大概也就15m,而原先会长时间不响应。当然这种改动的代价非常大,直接导致管理系统的更改,搜索引擎系统的对象映射等也受到了一些冲击。
     下午又进行MySQL培训,感觉数据库确实博大精深,虽然并不是DBA,也无意忘这个方向发展,但是作为开发人员,以后也要在这个方面提高一下了。