了解如何通过使用标准Java 8流和Speedment的In-JVM-Memory加速器将分析数据库应用程序加速1000倍。
Web和移动应用程序有时会很慢,因为后备数据库很慢和/或与数据库的连接施加了延迟。 现代UI和交互式应用程序需要快速后端,并且理想情况下没有可观察到的延迟,否则用户将继续使用其他服务,或者只会感到厌倦并完全停止使用该服务。
在本文中,我们将学习如何使用标准Java 8流和Speedment的in-JVM内存加速技术将分析数据库应用程序加速几个数量级。 最后,我们将运行具有代表性基准的JMH测试服,这些基准表明加速因子超过1,000倍。
以流查看数据库
速度是基于ORM的现代流,这意味着表被视为标准Java 8流。 在本文中,我们将使用“ Sakila”数据库,这是一个开放源代码示例数据库,可直接从Oracle 这里获得 。 Sakila示例数据库包含电影,演员等。 这是来自数据库的Java 8流的样子:
List<Film> secondPage = films.stream().filter(Film.RATING.equal("PG-13")).sorted(Film.TITLE.comparator()).skip(50).limit(50).collect(Collectors.toList());
该流将仅过滤出评级为“ PG-13”的电影,然后按电影标题对其余电影进行排序。 之后,跳过前50部电影,然后将接下来的50部电影收集到列表中。 因此,我们获得了按标题顺序排列的所有PG-13电影的第二页。 通常,我们还需要知道总共有多少部电影的评级为“ PG-13”,以便在我们的应用程序中显示正确缩放的滚动条。 可以这样完成:
long count = films.stream().filter(Film.RATING.equal("PG-13")).count();
使用数据库
Speedment将自动将Streams呈现为SQL。 这样,我们可以保持在纯类型安全的Java环境中,而不必编写SQL代码。 通过启用日志记录,我们可以看到第一个分页流将呈现给以下SQL查询(假设我们正在使用MySQL):
SELECT`film_id`,`title`,`description``release_year`,`language_id`,`original_language_id`,`rental_duration`,`rental_rate`,`length`,`replacement_cost`,`rating`,`special_features`,`last_update`
FROM `sakila`.`film`
WHERE (`sakila`.`film`.`rating` = ? COLLATE utf8_bin)
ORDER BY `sakila`.`film`.`title` ASC
LIMIT ? OFFSET ?values:[PG-13, 50, 50]
第二个计数流将呈现为:
SELECT COUNT(*) FROM (SELECT`film_id`,`title`,`description``release_year`,`language_id`,`original_language_id`,`rental_duration`,`rental_rate`,`length`,`replacement_cost`,`rating`,`special_features`,`last_update`FROM`sakila`.`film` WHERE (`sakila`.`film`.`rating` = ? COLLATE utf8_bin)
) AS Avalues:[PG-13]
因此,将流操作呈现为有效的SQL。 当在具有MySQL标准服务器配置的笔记本电脑类计算机上并行运行一千个查询时,它们分别在700毫秒和175毫秒的总延迟中完成。 如果您在考虑第二条SQL语句的效率如何,那么事实是数据库将基本上可以消除内部选择。
使用JVM中的内存加速
现在到有趣的部分。 让我们在应用程序中激活Speedment中的JVM内存中加速组件,称为DataStore。 这是通过以下方式完成的:
SakilaApplication app = new SakilaApplicationBuilder().withPassword("sakila-password")// Activate DataStore.withBundle(DataStoreBundle.class).build();// Load a snapshot of the database into off heap memoryapp.get(DataStoreComponent.class).ifPresent(DataStoreComponent::load);
启动应用程序时,数据库的快照被拉入JVM,并以堆外方式存储。 由于数据是非堆存储的,因此数据不会影响垃圾回收,并且数据量仅受可用RAM的限制。 如果有那么多的可用RAM,没有什么可以阻止我们加载TB的数据。
如果现在再次运行同一应用程序,则将获得22毫秒和1毫秒的总延迟。 这意味着等待时间分别减少了30倍和170倍。 必须说是一个重大改进。 但是,它还在变得更好。
使用JVM中的内存加速和Json
REST和JSON通常用于为最近请求数据的客户端提供服务。 Speedment有一个特殊的收集器,可以使用所谓的就地反序列化来收集JSON数据,从而仅从收集器内存中反序列化收集器所需的字段。 我们可以通过首先在pom文件中添加依赖项来依赖Json插件:
<dependency><groupId>com.speedment.enterprise.plugins</groupId><artifactId>json-stream</artifactId><version>${speedment.enterprise.version}</version></dependency>
然后,我们将插件安装在ApplicationBuilder中,如下所示:
SakilaApplication app = new SakilaApplicationBuilder().withPassword("sakila-password").withBundle(DataStoreBundle.class)// Install the Json Plugin.withBundle(JsonBundle.class).build();
如果我们只希望在json输出中使用Film字段“ title”,“ rating”和“ length”,则可以创建一个Json编码器,如下所示:
final JsonComponent json = app.getOrThrow(JsonComponent.class);final JsonEncoder<Film> filmEncoder = json.<Film>emptyEncoder().put(Film.TITLE).put(Film.RATING).put(Film.LENGTH).build();
该解码器是不可变的,可以在我们的应用程序中反复使用:
String json = films.stream().filter(Film.RATING.equal("PG-13")).sorted(Film.TITLE.comparator()).skip(50 * pageNo).limit(50).collect(JsonCollectors.toList(filmEncoder));
与处理整个实体相比,这为我们提供了2的额外加速因子。 JsonComponent可以做的不仅仅是将事情收集到列表中。 例如,它还可以使用就地反序列化来创建聚合。
使用In-JVM-Memory加速运行您自己的项目
自己尝试在JVM-Memory中加速很容易。 在这里可以找到免费的初始化器。 只需在所需的数据库类型中打勾,您便会自动为您生成一个POM和一个应用程序模板。 您还需要许可证密钥才能运行。 只需在同一页面上单击“请求免费试用许可证密钥”即可获得一个。 如果您需要更多帮助来设置项目,请查看Speedment GitHub页面或浏览手册 。
Real的速度有多快?
Speedment支持多种数据库类型,包括Oracle,MySQL,MariaDB,PostgreSQL,Microsoft SQL Server,DB2和AS400。 Speedment也可以使用Hadoop使用的Avro文件。 在此示例中,我们将运行MySQL。
众所周知,在Java应用程序中测试性能非常困难。 使用JMH框架,我编写了许多典型的应用程序,每个应用程序都运行了数十万次,并将纯MySQL和MySQL与Speedment的in-JVM加速器的结果进行了比较。 以下性能数据以每秒操作数(越高越好)的形式给出。
基准测试 | 纯MySQL | 具有Speedment in-JVM的MySQL | 加速因子 |
数一数 | 5,324 | 43,615,967 | 8,000 |
用过滤器计数 | 5,107 | 2,465,928 | 400 |
筛选 | 449 | 597,702 | 1,300 |
排序 | 109 | 171,304 | 1,500 |
分页 | 1,547 | 1,443,015 | 900 |
遍历所有 | 108 | 5,556 | 50 |
聚合 | 117 | 167,728 | 1,400 |
聚集过滤器 | 453 | 608,763 | 1,300 |
可以看出,在大多数情况下,带有Speedment In-JVM加速器的MySQL比纯MySQL的性能高出1,000倍。 观察到的最小加速因子是50倍,这仍然非常好。
测试环境
MySQL,5.7.16标准安装,MySQL JDBC驱动程序5.1.42,Oracle Java 1.8.0_131,Speedment Enterprise 1.1.10,macOS Sierra 10.12.6,Macbook Pro 2.2 GHz i7(2015年中),16 GB RAM。
基准代码
以下是基准代码外观的一些示例。 完整的基准测试应用程序可以在GitHub上找到 。 我鼓励您克隆它并运行它,以查看您自己的目标计算机上的加速因素。
@Benchmarkpublic String paging() {return films.stream().filter(Film.RATING.equal("PG-13")).skip(50).limit(50).collect(filmCollector);}@Benchmarkpublic String aggregationWithFilter() {return films.stream().filter(Film.RATING.equal("PG-13")).collect(sumLengthCollector);}
需要多少RAM?
速度通常比数据库本身更有效地将数据存储在RAM中。 基准测试中的Sakila数据库在磁盘上占用6.6 MB,但Speedment仅使用3 MB内存。 考虑到默认情况下,Speedment对所有列进行索引,而数据库仅对少数列进行索引,因此Speedment显着提高了内存效率。
加载数据需要多长时间?
Sakila数据库在不到1秒的时间内就被Speedment加载并建立索引。 Speedment可以在后台从数据库刷新数据,并且可以跟踪针对哪个数据库快照版本(MVCC)运行的流。
我自己的应用程序可以运行多少速度?
任何人都可以猜测,在特定项目中可以减少多少延迟。 是x10,x50,x100还是更多? 抓住机会,找出自己的项目可以提高多少速度!
旋转一下
在GitHub上了解有关Speedment的更多信息,并使用Speedment Initializer启动您自己的项目,并记住勾选“启用内存中加速”,并使用初始化程序也获得免费的评估许可证密钥。 在此处浏览有关Speedment in-JVM加速器的手册部分,或使用我的Twitter句柄@PMinborg
翻译自: https://www.javacodegeeks.com/2017/09/need-speed-access-existing-data-1000x-faster.html