当我们尝试用“Java 8 的方式”使用数据库是,如何去解决性能上的问题呢?
通过与 ZeroTurnaround 合作,我们为你带来了 Java Zone。你可以阅读这里的 8 个步骤的指南 ,来看看如何在编写代码时,利用跳过缓慢的应用程序重新部署的过程和实现应用程序分析来提高你的生产力吧!
Java 8 被发布的时候,人们开始让所有的东西变成流,没过多久他们就开始想象,如果可以将同样的方式用在数据库上,那将会有多棒。本质上数据库就是由大型的数据块以类似于表格的结构组织而成的。如 SQL 的 SELECT,WHERE,和 AS 语句向我们所表明的,这些结构对于过滤和映射操作很理想。(我们曾总结出)人们首先做的就是去向数据库获取到一个大型的数据集合,而后使用 Java8 中又新又酷的流技术来对这种数据进行处理。
有个问题立马就出现了,这个问题就是将来自数据库中的所有数据行转移到数据库中,其延时太长了。后果就是没能有多少盈余留下来对内存中的数据进行处理。即使你可以使用 Java 8 中新的工具做一些真正令人惊讶的东西出来,但这种好处却因为性能的消耗过甚而并不适用于数据库应用程序。
当我开始想 Speedment 开源 项目做贡献是,很快就意识到用 Java 8 的方式利用数据库的潜力,不过我们确实需要一个聪明的方式来处理性能问题。在本文中我会向你展示如何使用自定义的 Stream API 代理,在后台操作一个流,对于 SQL 查询的结果进行优化,以解决这个问题。
假设在远程数据库服务器上你有一张用户表,你想打印年龄大于 70 岁的用户名字。 在 Java 8 中使用 Speedment 可能会是这样:
final UserManager users = speedment.managerOf(User.class); users.stream() .filter(User.AGE.greaterThan(70)) .map(User.NAME.get()) .forEach(System.out::println);
看你这些代码你不仅要先打一个冷颤,我的程序会从数据库下载整个数据表,然后在客户端做过滤吗?如果我有 100,000,000 个用户会发生什么?网络延迟足以杀死应用程序!哇噢,事实并不是这样,就像我前面说过的。Speedment 在结束前会分析流。
让我们来看看幕后发生了什么。UserManager 中的方法 .stream() 会返回一个流接口的自定义实现,包含流的所有无数据一直到流关闭,元数据可以被终止的事件使用以优化流。当方法 .forEach 被调用时,管道看起来会是这样:
终止活动(在这个例子中 ForEach 将会向后遍历管道,看看它是否可以优化,首先它遇到从 User 到 String 的映射。Speedment 识别出它是一个 Getter 函数,User.NAME 字段是使用它来生成。Getter 可以解析到 SQL,所以终止活动切换到读操作,因为 NAME 列和映射被移除。
接着是 .filter。filter 也被识别出是一个自定义操作,在这个例子中是一个谓词。因为它是一个自定义实现,它可以包含 SQL 查询中需要的所有元数据,所以可以安全地从流中移除并附加在写操作)
此时终止活动继续查找管道,它会发现流的源头,一旦到达流的源头,读操作会解析到 SQL 并提交到 SQL 管理系统。最后 Stream<String> 会被最被的 .forEach 终止掉。上面的内容会生成确切的 SQL:
SELECT `name` FROM `User` WHERE `User`.`age` > 70;
Java 代码不需要改变和特殊的操作!
这是一个简单的例子,演示了流在执行前 Speedment 如何使用一个自定义实现使它简化。欢迎你查看 the source code 寻找更好的办法来优化这个技术。它真的帮助我们改善了系统性能,并在可以在 Java-8 的任何分布式环境下工作。