`
high0048
  • 浏览: 24429 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

海量数据查询的解决方案

阅读更多
作者:high0048

关键词:排序好的,分堆排序,最小值,最大值,中值,节点,虚拟化,数据迁移


传统数据查询之所以在大规模数据上性能达不到要求,是由于这些查询的排序工作是到查询之前的那一刻才进行的,这样做,要处理的东西就很多,就像临阵磨枪,所以很难。
如果我们在数据插入的时候,就进行大部分的排序,整理工作,(就好像先把房间整理好,再找东西就不难了)等到查询的时候只需要做很小的一部分工作,性能就会大大提升。
目前Key-Value的数据库十分火爆,由于其性能十分优秀,key的值就好像是地址,Value的值就是真正用到的数据,用的时候,直接从地址找数据。但是这样的数据库不能面对复杂查询。
如果在面对复杂查询的时候,我们能通过某种方式,得到key的集合,再通过key得到value,不就可以了吗?
那复杂查询到底怎么办呢?答案就是建立索引,而且是索引的集群!
索引的作用就是将数据的信息整理好,分门别类的放起来。找的时候就好找了。
现在考虑这样一种情况,我们有一个商品表,记录的条数有20亿。这样的记录数,一台机器肯定放不下,假定我们放在100台机器上,每台机器记录2000万条数据,这些数据是通过key-value的方式查询到的。
我们现在要查询类别为手机,价格为1000-2000元的商品,查出来要按价格由低到高排序,在20亿条数据中查。

怎样建索引,使得查询最快?
前面说过,排过序的数据查起来最快,那么我们就按照价格这个字段由低到高排序来建索引,每台机器的索引需要记录以下信息:
1. 索引的列名,这里是价格
2. 这台机器放的记录数,这里是2000万,在实际使用的时候,这个数是可以配置的,因为以后加进来的机器可能性能更好,放3000万数据也不成问题。
3. 这台机器上价格的最低值,最高值,最高值和最低值的平均值,中间那条记录的值。(内存中也要放一份,定期持久化,便于实时更新)
4. 索引所对应记录的id
排序是从低到高排的,先按列值排,再按id号排(id其实就是地址信息,按序排在一起,查起来是连续的地址,性能更好)。
这样组织好的索引,具备以下特征:
1. 排在前面的列值,一定比排在后面的列值低。
2. 对应同一列值,序号小的机器上的任何记录都要比序号大的机器上的最小值还要小,反之,序号大的机器上的任何值,比序号小的机器上的最大值还要大。
注意,我们这里所说的“机器”是虚拟的“节点”,“节点”是由 机器号 和 表名.列名 唯一确定,“节点”上的记录数是确定的,2000万,3000万,更多或更少。这样做的好处是:
1. 同一台物理机上可部署多个“节点”。
2. “节点”的高低次序不依赖于具体的机器IP或物理地址,便于迁移。
针对某一个列名,如价格,下面有若干个有序节点,组成一个价格有关的“拓扑”;针对另一个列名,如商品名,下面有若干个有序节点,组成一个商品名有关的“拓扑”;拓扑(就可以看做列与列之间)之间完全分离,互不干扰,即便某个拓扑下的节点,和另一个拓扑下的节点,有可能部署在同一台机器上。
这样整个集群里面其实只有列名,也就消除了join联表查询,并且彻底消除了每一次分表,分库之后重写查询语句的苦恼! 之所以用 表名.列名,完全是为了区别不同表遇到同一列名的情况;另一方面,也是为了和数据库的传统概念相吻合,便于理解。
下面我们看看前面提到的查询:查询类别为手机,价格为1000-2000元的商品。
这个查询涉及到2个列:
1. 类别
2. 价格
大致步骤:
1. 可以分2个线程,分别查这两列。
2. 选取结果数少的,归并结果。

类别为手机,因为“类别”这个列已建索引,我们只要到 类别=手机 的若干台机器上去找,记住,所有“类别=手机”的分布都是连续的哦。所以我们只要找:从第几台机器的第几条记录,到第几台机器的第几条记录,然后连续的取出来就可以了!
同理,价格为1000-2000元的商品,其索引也是连续的。所以我们只要找:从第几台机器的第几条记录,到第几台机器的第几条记录,然后连续的取出来就可以了!


你可能会问,为什么这些索引都是连续的?插了新数据之后如何保证还是连续的?

每个节点都会有一个临时的区,存放白天插入的数据,新的数据插入和各个节点的平均值比较,----(1.还是中间记录那个值?为了以后数据迁移量最小化)(2.应该取平均值和中间记录那个值的较大者),这样尽量把数据放在后面,迁移量会少)(3.应该和最大值比较,小于最大值放本节点,大于最大值放下一个节点)---- 选取比较值最小的,定位到这个节点,然后插入排序。这样,每个节点的记录数会改变,中间值也会改变,最大最小值也可能改变,不过这个几率小。晚上负载小的时候,再进行迁移,使每台机器记录数达到当初设定的那个值。
迁移的时候,将多出来的数据截下来,整个放入下一个节点,只许向右迁移(考虑到数据总趋势是增加的),左边节点的有可能达不到设定的值,不管他,等下次插入数据的时候可以填充进这些空位,这样做也是为了减少迁移量。
迁移关注两个阶段:
1. 机器之间数据是否已成功迁移。
2. 迁移之后,这些数据是否已经排好。
只有这两个阶段都成功之后,才使用新的数据。否则还是使用迁移之前的数据。

各个节点也应该定期检查:
1. 是否本节点的数据已经排序正确。
2. 本节点的最小值是否比上一个节点的数据的最大值还大?不是的话,将这条数据迁到上一个节点。本节点的最大值是否比下一个节点的最小值还小?不是的话,将这条数据迁到下一个节点。

查询的情形,无外乎

select * from T where column=<a> and ... order by column group by column.


在索引里大致对应这样的语句:
Select (表名.列名) where (表名.列名1=值1,表名.列名2=值2,…,表名.列名x=值x ) order by (表名.列名a),group by (表名.列名b)




如果列名是像性别这样的bool值的建索引一定是相同值跨好多机器的,这样的话,按照id排序。那最近如果某人从男修改成女,放在哪里好呢?为了性能,应该是放在id相近的节点,可是节点又没有id信息,id只能在同一个节点内排序。怎么办?应该放在最后面的节点的末尾,减少数据迁移。
不过觉得bool类型的不要做索引比较好,太浪费空间,效果又不大。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics