当前位置: 首页 > 科技观察

使用Spring对Postgres实现可扩展写入_0

时间:2023-03-13 04:07:51 科技观察

使用Spring为Postgres实施可扩展的编写扩展的产品和组织对您的流程和基础设施提出了新的要求。本文重点介绍我们公司如何应对基础架构扩展中的众多挑战之一:使用Spring和SpringData实现对Postgres数据库的可扩展写入。随着我们用户群的增长,我们开始遇到一些性能问题,主要受限于我们的上游Postgres数据库。我们的RPS(每秒请求数)在短短几个月内从<50增加到超过180,我们开始遇到SQL连接超时、连接丢失和延迟显着增加等问题。这会导致糟糕的客户体验,这是不可接受的。所以我们着手研究如何消除这些Postgres瓶颈。我们很快意识到,太多的周期花在了数据库写入上,这阻塞了系统。每次写入Postgres都是一次调用,这意味着如果我们想将50行保存到数据库中,它将每行调用1次,而不是执行一次SQL调用来保存所有这50行!根本原因:在Hibernate中使用IDENTITY生成ID值为什么不能批量更新?原来,这个问题与我们如何使用Hibernate为数据库中的实体生成标识符值(即主键)有关。我们使用的方法需要从IDENTITY列中检索值,这些列在新实体插入数据库时??由Hibernate动态维护。我们为新资源写入数据库是在不指定id(主键)的情况下完成的,而是使用GenerationType.IDENTITY。这就是我们的Spring实体的样子:Kotlin@Entity@Table(name="entity")dataclassEntity(@Id@GeneratedValue(strategy=GenerationType.IDENTITY)valid:Long?=null,valmetadata:String,):TenantEntity()使用这种策略,使用ORM创建和更新现有资源是微不足道的:如果没有传递id,则会创建一个新行。如果传递了id,现有行将被更新。听起来容易吗?我们也是这么想的!而且它似乎运行良好,直到后来我们意识到使用IDENTITY导致了严重的性能问题。这种策略的缺点是批量更新不起作用。这给我们带来了一个大问题,因为我们所有的实体都是使用IDENTITY标识符值生成的。对于每个现有的表和相应的实体,我们必须将策略从IDENTITY更改为支持批量插入语句的不同策略。从IDENTITY迁移到基于序列的ID生成在研究了可用于支持批处理的实体的其他生成类型后,我们发现了Hibernate的基于序列的标识符值生成。该策略由底层数据库序列支持。Hibernate请求序列中的下一个可用ID以获得资源的新ID。虽然此策略的基本机制超出了本文的范围,但结论是此基于序列的策略将为我们实现批量插入。现在我们需要弄清楚如何从现有的IDENTITY策略迁移到新的基于序列的方法。经过进一步调查,我们意识到现有表已经有一个Postgres序列。因此,如果我们有一个这样定义的表:SQLCREATETABLEIFNOTEXISTSentity(idBIGINTGENERATEDBYDEFAULTASIDENTITYPRIMARYKEY,...)将创建一个名为entity_id_seq的序列!您可以运行以下SQL命令来检查序列是否存在:SELECT*FROMpg_sequenceWHEREseqrelid='entity_id_seq'::regclass;由于我们可以轻松访问Postgres表的序列,因此我们可以进行非常局部的更改,以使用基于序列的策略来生成id。对于每个实体,我们只需要改几行代码就可以解决性能瓶颈。更新后的实体看起来像这样:,generator=SEQUENCE)@SequenceGenerator(name=SEQUENCE,sequenceName=SEQUENCE,allocationSize=50)@Column(name="id")valid:Long?=null,valmetadata:String,):TenantEntity()AllocationSize和Sequenceincrementsize这里需要说明的一点是Hibernate中的allocationSize属性需要和Postgres中底层序列的incrementsize一致。这是为了让Hibernate和底层序列在它们拥有的id方面“同步”。这也可以防止多个服务器写入同一个表的分布式架构出现任何问题。默认情况下,Postgres序列的增量大小为1。我们编写了一个非常快速的迁移来更改它以匹配我们的allocationSize:ALTERSEQUENCEentity_id_seqINCREMENT50;现在Hibernate每50次插入只需要调用1次来获取ID列表。插入这50行也只需要1次调用。这是我们从这个问题中得出的结论:与Hibernate一样,尽快开始使用基于数据库序列的身份值生成,特别是如果您预见到写入次数会增加。保持allocationSize和底层Postgres序列增量大小参数相同,以避免id冲突并支持分布式系统。最后,这是我们实施该更改后RPS从近180增加到大约90的屏幕截图。原标题:ScalableWritestoPostgresWithSpring,作者:AdityaBansal