在关系型数据库中运行计算

http://www.infoq.com/cn/news/2014/03/relational-database-calculation

近日,JOOQ的官方博客上发表了一篇文章,针对Stack Overflow上“如何使用Hibernate映射处理庞大的数据表”这样一个问题,作者认为有必要提醒下开发人员,不要犯Java开发人员编写SQL时常犯的十个错误中的第二项错误:在Java内存中处理数据。

Stack Overflow上的问题可以归结为:从下面的中型表中计算出每个应用程序ID对应多少个状态为0或1的文档。用Hibernate该如何实现?

AppID | DocID | DocStatus 
------+-------+----------
1     | 100   | 0
1     | 101   | 1    
2     | 200   | 0    
2     | 300   | 1
...   | ...   | ...

“NO!不要用Hibernate!你要用SQL。Es-Queue-EI!……”,作者认为,有许多简单的方法可以让SQL服务器来运行这种查 询,而且时间很短,又不用在聚合之前将所有的数据加载到Java内存。他分别使用GROUP BY、嵌套查询、SUM()和PIVOT给出了四种实现方式,并认为其中任何一种的性能都会在数量级上超过任何基于Java的实现。文章的结尾这样写道:

任何时候,只要合适就使用SQL!能用SQL的地方远比你想象的多。

该文在reddit用户之间引发了激烈的讨论。ggtsu_00认为:

……如果计算减少了返回结果的行数,那么最好在数据库里计算。不过,许多计算是后处理或格式修改,这些最好是在应用服务器上进行。

对于ggtsu_00的观点,lukasedar进行了补充,认为“争论的焦点是通过网络在处理数据的节点之间传输的数据量”。 Grauenwolf则表示,如果将该观点中的“返回结果的行数”改为“返回结果的行数或列数”,那么他也赞同。而该观点的后半部分则引发了进一步的争 论。ItsMeCaptainMurphy认为:

这要看你做什么,构建数据库通常是用来尝试并行的,对于行级操作尤其如此。……而且你的数据库服务器的性能可能比Web服务器或客户端更强大。那么,有些事情确实是最好在应用程序端做,但并不是所有情况,甚至不是多数情况。

不过,emn13则认为这与数据库服务器的性能无关,而与代码性能相关:

本地或近似本地代码的性能通常是SQL的1000倍,而且可能更高。即使是像Ruby或Python这样相当慢的语言在简单表达式求值方面也可能比大部分SQL服务器要快。

SQL不是一个很好的通用计算器。如果计算没能减少返回结果的行数,就不能想当然地认为一台高性能的数据库服务器实际上会超过一部手机。

……

总而言之,由于大部分计算都很简单,所以没有减少数据也没关系。但当计算代价很高时,SQL通常是缓慢的。

为了使自己的观点更有说服力,他结合自身的经验作了进一步的说明:

1000倍这个数量级是我在MS SQL SERVER上实现一个有向图节点计分算法时观察到的,不是假想的场景。

Ulukai对上述观点表示了赞同,他还补充说

如果有非常复杂的逻辑需要执行,那么你应该仔细考虑。比如,我不会在数据库代码中执行“最短路径”算法,除非它获得原生支持。

在关系型数据库中进行计算,除了应用场景的问题,还有知识结构和使用习惯的问题。人们已经花了很多时间和精力来学习ORM框架的所有最细微的细节,所以他们真的不喜欢他们应该更好地学习SQL这样的建议。但crimson_chin认为:

学习任何一个而不学习另一个都会让你处于不利地位。如果学了SQL没有学ORM,那你就要面临代码可能过于冗长且难以维护的风险。如果你学了ORM没有学SQL,那么你就要面临自我折磨的风险,因为一个查询为了获取项的名称列表却拉回了200列。

但同时,他认为数据库代码难以测试、管理和维护。因此,只有在可以明确地知道是最佳实践的时候,他才会使用数据库的特性来进行开发。

总之,JOOQ的博文虽然引发了一场讨论,但文章本身的内容似乎没有多大的争议。至于什么时候应该在关系型数据库中进行计算,什么时候应该在应用程 序端进行计算,大家也有一定的共识。具体做法则要视应用场景,并根据SQL、ORM各自的优缺点进行综合分析和测试,而这当然离不开对SQL和ORM的学 习和使用经验。

This entry was posted in Best Practices, DB. Bookmark the permalink.

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s