【范围关联】算法探讨

声明:文中观点为作者的个人观点、不代表官方、如需更多帮助,请联系Pivotal官方·转载必须注明出处

首先解释一下何为范围关联:关联条件是一个类似Between And的语句,了解Join的码农都很清楚,这种Join条件是无法走Hash Join算法路径的,
     作为一个码农,如果你说你懂SQL而不清楚什么叫做Hash Join,建议你先去补充一些基础知识,不然接下来的内容你就没必要继续阅读了。
     很容易理解为什么这种 Join条件无法走 Hash Join算法路径,因为 Hash算法要求数据必须能够映射到 Key Value模式,而范围值是不确定的,当然
     就无从映射Key Value了。
在数据库中,Join算法无外乎Hash Join|Merge Join|Nestloop Join,从计算复杂度上来说只有Hash Join是线性的,也是多数情况下的最优选择,这
    里不去讨论Hash Join与Merge Join之间的最优场景,在GP数据库中,绝大多数情况下都是Hash Join占优。因此,我们总是希望Table之间的Join
    能够选择Hash Join这种优秀的匹配算法。下面的内容就展开讨论上面提到的无法走Hash Join的场景。
我第一次遇到这个问题,是在一次GP的POC中,用户为某曾经很牛的游戏公司,他们有一个IP信息表,大约1W条左右的数据,主要信息就是开始
    IP段、终止IP段、所属地区,再有就是一张访问日志表,其中需要分析的信息就是IP地址,用户希望统计日志表中IP信息在各个地区的分布情况,
    低级程序员自然的想法就是Ip_Addr  Between Ip_Start And Ip_End(用户允许、且已经将IP类型翻译为数字类型),用户告诉我,他们在Oracle
    RAC中根本跑不出来,日志表每天的数据量其实也就5000W的数量级。为啥跑不出呢,先卖卖关子,来说说我的测试经过,我看到这个SQL的时
    候没想太多,直接看了一下执行计划,我傻了,执行计划是一个Nestloop Join,再看Cost,我更傻了,一眼看不出数量级了,等于5000W的数
    据要被扫描1W次,那就是5000Y记录的扫描量,当然,最初我并没打算帮他们换一个思路去解决这个问题,我们(主要是当值领导)希望,如果
    半配DCA能够在几个小时内跑出来,我们就说GP可以做到,客户可能也就因此考虑购买GP。但是,任务扔到后台跑了快10个小时之后,仍然没
    有结果,我觉得需要换个思路了。
最初我想把1W条数据缓存到C函数中去,在C函数中用静态数组存入排序后的IP范围数据,每条记录使用二分法进行查找匹配,但那时从没在GP中
    写过C函数,当然,如你所知,GP的官方资料中也没有这方面的指导意见,而作为野鸡从业人员,我也无法获得GP的内部神秘资料,至于后来
    我从PostgreSQL中学会了如何用C去实现这一想法,那已经是很久之后的事情了。于是我不得不换个思路,很显然,我想到了把IP配置表展开,
    我发现IP配置表中只涵盖了2Y左右的IP地址(IPV4理论最大值也就4Y左右),这个IP地址的展开,也是一个技巧,因为在GP中你不可能用循环去
    做,于是我制作了一个从1到N的序列种子表,序列种子对IP段做几次关联分拆,当然,这个过程很快,经过测试,这一方案统计1天的日志数据
    只需要10秒以内,而统计1个月的数据也就1分钟不到。我比较担心这个思路会毁掉那次测试的生意机会,就把情况和当时的领导细说了一遍,
    领导认为,即便生意不成,也要彰显我们的技术能力。结果很显然,人家拿着我的方案回去Oracle RAC上继续玩下去了。这就是教训啊,你们
    这些无良的买家,初次遇到这方面的问题,我苦苦思考了一天一夜才找到这个办法,就被你们无情的窃取,且未支付一分钱的酬劳。
从那次之后,我就开始琢磨这类问题的解决办法,不久之后,就用C函数成功的实现了想象中的思路,这里简单描述一下这个方案的思想。已知的
   前提条件,IP段之间没有重叠,这很重要,不然一条日志就会匹配多条IP段,那个C函数也就需要返回多条记录,事情会变得复杂很多。首先,
   将IP段表中数据排序后COPY到所有Segment节点,然后把节点上的文件内容读入C函数的静态数组中,再用已经读入的IP段匹配每个IP地址,然
   后神奇的效果出现了,函数基本不消耗计算资源。这里不附赠源代码。
后来在GP群中遇到一个曾经POC过的某运营商开发商的朋友,他们的需求有类似之处,差不多1W个IP段,而需要识别的IP地址是多值情况,也就是
   带有分隔符的多个IP连成一个字符串,其实思路是相似的,只是那个C函数需要处理一个更复杂的IP串。我很直接的告诉了他,我的解决思路,并
   附赠了Python函数的代码实现,但我并没有提供C函数的代码,因为那是我利用休息时间辛勤劳动的成果,没理由免费赠送,经过测试,同样的
   算法Python的性能是SQL的20倍,而C的性能是Python的50倍,他们要处理的是100Y级别的数据,我很清楚这50倍性能的重要性,但是,我还是
   不愿看到自己的成品被作为免费午餐。那个高性能的C实现,至今还躺在我的电脑中,没有产生1分钱的价值。
能够激发我写这段文字的却是最近一个快递公司的开发工作,他们有个物料使用量的报表需求,乍看起来与以上两者甚为相似,但仔细一看,发现
   差别很大。面单发放记录数量级在800W,已用面单数量级在10Y(还是有时间范围的),这个范围配置表真的有些大,发放记录是有交叉的,单号
   开头可能有几个字母的。首先他们告诉我,对于使用量的统计,他们用几台机器搭建的Hadoop算了2天才出结果,据说采用的方式,就是详细展开。
   更进一步的需求,获取每笔发放记录中未被使用面单的详细单号列表,据说他们3年来一直想做而做不了。这个情况,让我觉得,之前的经验不能
   照搬了。我努力的思考了几分钟,并询问了一堆看似无关紧要的问题,最终我发现,其实数据是有那么一点规律的,那就是,多数的发放都是以
   1000为单位的。当然,也有一次发放100W的,也有只发放50或者100的,但那是少数,经过思考,我发现,找到方法了,可以避开Nestloop Join
   了,于是我努力思考一个Perl函数去分拆单号段,以1000为单位进行分拆,将单号摸去末尾3位数做等值匹配,再辅以号段限制,问题得到了很好的
   解决,800W对10Y的数量级,统计得到500W数量级的结果,耗时100秒,也就是2分钟不到,半配DCA上得到的性能比原先提高了1000多倍。这
   才是激发我思考所有此类问题的动力。至于那个未使用列表,我想我已经有了很好的思路,但我不打算说出来,也不打算把我的Perl函数代码附赠,
   有能力理解我的思路的人自然可以理解。
最后想说,每个问题都是有解决办法的,只是,更需要一个能解决这个问题的人,带着你们走另外的路,而不是在原来的路上继续前行。

声明:文中观点为作者的个人观点、不代表官方、如需更多帮助,请联系Pivotal官方·转载必须注明出处

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/11022757/viewspace-1242299/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/11022757/viewspace-1242299/

你可能感兴趣的:(python,数据库,大数据)