启动Predator-预编译的数据存储库


今天,对象计算公司(OCI)的Micronaut团队推出了Predator ,这是一个新的开源项目,其目标是显着改善微服务和无服务器应用程序的数据访问的运行时间和性能(从内存)(从内存中)。与GORM和Spring Data之类的工具相比。


数据访问工具的历史记录


我们可以跟踪自2004年Ruby on Rails推出ActiveRecord子系统以来的数据存储库模板的历史记录,该API彻底改变了我们对开发人员生产力的数据访问理解。


在2007年,Grails团队首先为JVM引入了类似于ActiveRecord的API-GORM(Grails的一部分)。 GORM依靠Groovy的动态特性在Hibernate之上实现搜索方法,并为JVM用户提供了相同的生产力优势。


由于GORM依赖于Groovy语言,因此Spring Data项目创建于2011年,该项目允许Java开发人员在界面中定义搜索方法(例如findByTitle ,并在运行时自动实现查询逻辑。


数据访问工具如何工作


所有提到的实现都使用相同的模板,该模板在运行时构建项目实体的元模型,以对实体类之间的关系进行建模。 在Spring Data中,它是一个MappingContext,在GORM中,它也称为MappingContext。 通过使用反射扫描类来构造它们。 (这里的命名相似并非偶然。2010年,我与Spring Data团队合作,尝试为一个项目重新创建Java的GORM,该项目最终变成了今天的Spring Data。)


然后,使用正则表达式分析和框架逻辑的组合,使用此元模型在运行时将诸如bookRepository.findByTitle("The Stand")类的搜索表达式转换为抽象查询模型。 我们需要一个抽象查询模型,因为查询的目标方言对于每个数据库(SQL,JPA-QL,Cypher,Bson等)都是不同的


Micronaut存储库支持


自从一年多前推出Micronaut以来,我们被问到的主要缺失功能是“ GORM for Java”或Spring Data支持。 如此众多的开发人员爱上了这些工具所提供的生产力,以及对定义框架实现的接口的便捷性。 我想说,Grails和Spring Boot的大部分成功都可以分别归功于GORM和Spring Data。


对于使用Groovy的Micronaut用户,我们从一开始就获得了GORM支持,而Java和Kotlin用户则一无所有,因为他们需要自己实现存储库。


坦率地说,为Micronaut添加一个配置Spring Data的模块在技术上是可能的,并且坦率地说更容易。 但是,按照这条路径,我们将提供一个使用Micronaut试图避免的所有方法实现的子系统:代理的广泛使用,反射和高内存消耗。


引入捕食者!


Predator是Precomputed Data Repositories的缩写,它使用Micronaut API在执行之前进行编译(AoT,提前进行),以转移实体的元模型并将搜索表达式(例如findByTitle )转换为适当的SQL或JPA-QL,供您的编译器使用。 结果,该查询执行了一个非常薄的程序运行时层而没有反射,并且仅保留它来运行查询并返回结果。


结果是压倒性的……冷启动大大减少了,我们获得了惊人的低内存消耗,并且性能得到了极大的提高。


今天,我们在Apache 2许可下开放了Predator的源代码,它将为JPA(基于Hibernate)和带有JDBC的SQL提供两个初始实现(计划在将来提供更多功能)。


JDBC实现令我最满意的是,因为它完全独立于反射,不为数据访问级别使用代理和动态类加载,从而提高了性能。 运行时层非常轻巧,即使手工编写的等效存储库代码也不会更快地执行。


表现掠食者


由于Predator不需要在运行时执行任何查询转换,因此性能提升非常明显。 在云计算利用率的世界中,您需要为应用程序的运行时间或执行单个功能付出时间,而开发人员通常会忽略其数据访问机制的性能。


下表总结了与其他实现相比,简单搜索表达式(如findByTitle可以预期的性能差异。 所有测试都是在相同条件下使用8核Xeon iMac Pro的测试平台进行的,这些测试是开放的,可以在存储库中找到:


实作每秒操作
捕食者JDBC225K次/秒
捕食者JPA13万次/秒
春季数据JPA90K次/秒
GORM JPA5万次操作/秒
Spring Data JDBC查找器不受支持

是的,您没看错。 使用Predator JDBC,您可以预期性能比GORM提高近4倍,而Spring Data则提高2.5倍。


即使使用Predator JPA,与GORM相比,您也可以指望将性能提高2倍以上,与Spring Data JPA相比,可以提高40%。


查看使用Predator时与替代方法相比执行堆栈大小的差异:


捕食者:



捕食者JPA:



春季数据:



GORM:



在执行请求之前,Predator JDBC仅使用15帧,而Predator JPA使用30(主要是由于Hibernate),相比之下,Spring Data或GORM中使用了50多个堆栈帧。 这要归功于不使用反射的Micronaut AOP机制。


较短的stackrace还可简化应用程序调试。 在编译期间执行大部分工作的优点之一是,可以在应用程序启动之前检测到错误,从而大大改善了开发人员的体验。 我们会立即得到编译错误,而不是最常见错误的运行时错误。


编译时间检查


存储库模板的大多数实现仅依赖于在运行时执行所有操作。 这意味着,如果开发人员在定义存储库的接口时犯了一个错误,则在实际启动应用程序之前,错误是不可见的。


这使我们失去了Java用于类型检查的某些好处,并且我们的数据经验很差。 捕食者不是这种情况。 考虑以下示例:


 @JdbcRepository(dialect = Dialect.H2) public interface BookRepository extends CrudRepository<Book, Long> { Book findByTile(String t); } 

在这里, BookRepository我们声明了对名为Book的对象的请求,该对象具有title属性。 不幸的是,此声明中有一个错误:我们将findByTile方法命名为而不是findByTitle 。 除运行此代码外,Predator不允许您的代码编译时显示一条错误消息:


 Error:(9, 10) java: Unable to implement Repository method: BookRepository.findByTile(String title). Cannot use [Equals] criterion on non-existent property path: tile 

可能时,会在编译时检查Predator的许多方面,以确保运行时错误不是由错误的存储库声明引起的。


捕食者JDBC和GraalVM底物


Predator满意的另一个原因是,它与本机GraalVM Substrate图像兼容,并且在构建过程中不需要复杂的字节码转换,这与在GraalVM上进行Hibernate操作不同。


通过完全消除数据访问层的反射和动态代理,Predator大大简化了与在GraalVM上运行的数据一起使用的应用程序的创建。


Predator JDBC示例应用程序可以在Substrate上正常运行,并且由于运行时层更薄,因此您可以创建比Hibernate少得多的本机映像(少25 MB!)。


当我们为Micronaut 1.2实现Bean验证规则编译时,我们看到了相同的结果。 删除对Hibernate Validator的依赖关系后,本机映像大小将减少10 MB,而JAR大小将减少2 MB。


这里的优势是显而易见的:通过在编译期间进行更多工作并创建更紧凑的运行时,您可以获得更小的本机映像和JAR文件,从而在通过Docker进行部署时导致更小,更容易地部署微服务。 Java框架的未来是功能更强大的编译器和更小,更轻便的运行时。


捕食者与未来


我们刚刚开始与Predator合作,并对其带来的机会感到非常满意。


最初,我们从对JPA和SQL的支持开始,但是在将来,您可以期望对MongoDB,Neo4J,Reactive SQL和其他数据库的支持。 幸运的是,这项工作要简单得多,因为大多数Predator实际上都是基于GORM源代码的,我们可以为Neo4J重用GORM逻辑,为MongoDB重用GORM,以比您期望的更快地发布这些实现。


Predator是将Micronaut Core中的各种构建块结合在一起的最终产物,从可以用于生成Swagger文档的AoT API到相对较新的Bean自省支持,它使您可以在运行时分析对象而无需进行反射,从而使其得以实现。


Micronaut提供了令人惊奇的事物的构建块。 捕食者就是这样的事情,我们才刚刚开始研究Micronaut 1.0的一些有前途的功能。


更新:令人震惊的公告后,Spring Data Killer被重命名为Micronaut Data: https ://micronaut-projects.imtqy.com/micronaut-data/1.0.x/guide/

Source: https://habr.com/ru/post/zh-CN460839/


All Articles