【威尚网络】数据库分表浅见

来源:互联网 发布:java ansi编码转utf8 编辑:程序博客网 时间:2024/04/29 21:34

 http://www.veisun.com/newsinfo/?id=3901

(整理:网站建设网站制作公司-VeiSun.Cn)从建房子说起
      记得小时候,我爸,大伯,叔叔都是在祖屋里住着的,后来我爸和叔叔都在村子里建了房子,都搬出来了。这也是很顺理成章的事情,因为原有的祖屋已经不足以住下我们的一大家子人了。

      不过,这时就会有一个问题,就是在不知情也没有其他信息的前提下,怎么根据姓名来判断他是住在祖屋还是新屋呢?最简单最笨的方法,当然是先在祖屋找,没找到就再到新屋找,但是新屋比较多或者两个屋子之间相距比较远的话,这个成本就变得很高,不合算。

      为了降低这个成本,我们可以事先用一个本子(索引表)记录下每个人住在哪里,这样只要一查本子就知道了。

逐步分表
      和房子不够就需要重新建一处房子一样,当一个数据表记录太多太大,从而影响了查询效率的时候,我们就应该考虑分表,把新增的数据都保存到另一个表上,依次进行下去。和上面找人的问题一样,我们应该有一个什么样的策略来判断某个ID的记录在那个表上。这里有几种方式:

一:把记录ID和数据表名的对应关系保存在一个独立的表(找人问题中的本子)中,可是这样当记录很多的时候这个表的记录就会很多,虽然这个表结构很简单,但是也会影响到查询效率。还有就是在插入删除的时候都得关联这个表的操作,不方便。

二:一次性把现有的所有数据根据某个字段(通常是ID)按照某种规则重新散列到各个表(包括新建的)上。这样原有程序改动不大(如果设计合理的话,可能改动的只是一个配置的参数),而且每个表的访问压力相当(如果Hash规则足够随机的话),不过这一次性的数据迁移代价可能很高,有风险。

三:如果ID是一个自动自增主键,那么我们就可以把每个表的初始ID记录在配置文件里,这样我们的程序就很容易根据ID找到对应的数据表了。这种方法没有数据大规模迁移的麻烦,可是每个表的访问压力会有比较大的差别。

      第一种显然是不能用的,第二种和第三种那种好点,就应该根据实际情况来确定(通常可能都是第三种好点)。

一次性分表
      逐步分表的思想是在需要的时候,新增一个表,以保证查询效率。但是如果我们在系统设计的时候就可以对将来的数据量有一个比较准确的估计,我们就可以在一开始的时候就设计N个分表,如果这个N值较大的话,最好独立成一个数据库。在插入数据的时候,我们根据记录ID把数据Hash到某个表上,这样在操作数据的时候就可以根据ID来计算表名了。

       这种做法很简单,省了很多功夫,不过需求并没有像预期的变化,原来的N值还是小了,这时就会比较麻烦,得执行逐步分表的步骤。而如果发现N值设置太大了,这样就会导致数据表利用率太低,有点浪费了资源。(例如,在mysql中myisam类型的数据表,一个表对应三个文件,这样N值太大就是说文件过多了,同样也会影响到效率)

分表带来的问题
      对于很多的互联网应用,我们主要关注的是查询的优化(经常不惜以空间换时间),但是我们在查询的时候可能并不是只根据ID来查询,而且还通常需要分页。我们上面的分表却无法胜任这些,所以我们得另外想办法,例如把结果缓存起来(如内存表,文件,APC,Memcache等)。不过不管怎么缓存,总感觉有点别扭,不够完美。

       假如有这样三个对象表:用户表,影片表和Tag表,他们各自对应的主键ID为:user_id,movie_id和tag_id,他们两两之间的对应关系是多对多的(例如一个用户可以收藏多个影片,可以为每个收藏的影片填写多个tag)。现在要做的就是可以根据其中的任一个对象来迅速查找其它两个对象的相关信息,数据表结构应该怎么样设计?

       如果每次只是对一种ID进行分表,显然我们要建立三种散列表,如果考虑把散列表独立成库的话,就得用三个数据库,管理和操作都有一定的麻烦。后来灵光一闪,想到了一种比较好的解决方法:表中除了保存user_id,movie_id和tag_id外,另外加了一个flag字段,这四个字段组成组合主键(当然表还可以保存其他的信息,特别适合保存一些统计信息,排序的时候很有用)。其中flag字段标志此记录是根据哪种ID进行散列的,不妨设flag=1时根据user_id散列,flag=2时根据movie_id散列,flag=3时根据tag_id散列。

       这时假如我们要根据movie_id查找关联的用户和tag_id,这时我们的where条件可以这样写:

               SELECT * FROM {$table} WHERE movie_id='{$movie_id}' AND flag='2' ...

(注:假设我们根据movie_id的值计算得到相应的Hash表名为$table,实际上这也是很容易得到的。)

其他的两种也一样,这显然对查询很有利。当然这样设计之后,对数据的增删改操作就有点麻烦了(通过把对散列表的操作封装在一个类里面可以减少很多麻烦),不过为了查询效率的提升,这种牺牲应该还是值得的。

一些思考
       接触关系数据库越多,就越发现关系数据库也有不少的局限,例如在查询效率和并发连接数方面,关系数据库通常会比key-value型数据库(例如MemcacheDB,Tokyo Tyrant,Bigtable等)差很多,而且key-value型数据库很容易和Memcache结合,以实现持久化存储,从而很容易做到分布式存储。也许以后会有人考虑开发一种专门存储JSON格式的数据库,呵呵~

       还有就是,以前觉得Mysql很小,相比那些SQL Server,甚至oracle等等,但是现在觉得Mysql已经很庞然大物了,于是有人要给mysql瘦身,弄了个Drizzle出来。(就像我们觉得apache已经是庞然大物了,所以我们弄了很多轻量级的服务器,如Lighthttpd,Nginx等,来实现相应特定的功能)

       当然,所有的这些并不是说关系数据库已经“死了”,而只是细化了分工,从而弥补了关系数据库某些方面的不足。所以总的来说,“关系数据库已死”这样的论断还有点言之过早了。(整理:网站建设网站制作公司-VeiSun.Cn)

 

原创粉丝点击