如何更快地将单个行插入 MySQL 表?
Posted
技术标签:
【中文标题】如何更快地将单个行插入 MySQL 表?【英文标题】:How to make insert individual rows into MySQL table faster? 【发布时间】:2022-01-16 02:32:20 【问题描述】:我有一个运行 Spring boot + JPA + Hibernate 的服务器。我正在使用 mysql 数据库(默认使用 InnoDb 引擎)。 该实现从我在 Internet 上搜索的许多文章中汲取灵感。 我已经实现了 REST API 来促进动态构建网站。 我想将所有 API 请求记录到日志(审计日志)中。所以当API被调用时, 我将请求方法名称和一些参数存储到 MySql 的审计日志表中。 就在我从 API 返回之前,我还通过更新相同的记录来存储响应。
当我使用 Web 应用程序客户端和 Postman 发出 API 请求时,我正在查看 Hibernate 的代码日志。 我注意到对于每个 API,插入和更新平均需要 150 毫秒到 200 毫秒。 事实证明,这对于获取的信息非常少的 API 来说代价高昂。
所以我想知道如何加快插入速度,使我的插入/更新时间少于 10 -20 毫秒。
我的 Auditlog 实体是
@Entity
@Table(name="auditlog")
public class AuditLog
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false, updatable = false)
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
private Date created_at;
@Column(nullable = false)
@Temporal(TemporalType.TIMESTAMP)
@LastModifiedDate
private Date updated_at;
@NotBlank
private String methodName;
private String param1;
// Text field with private information like password masked
@Column(length = 65535, columnDefinition = "text")
private String request;
// Text field
@Column(length = 65535, columnDefinition = "text")
private String response;
private Integer result;
... // getters and setters
我的 AuditLogRepository 是:
public interface AuditLogRepository extends JpaRepository<AuditLog, Long>
在我的 REST API 控制器中,我正在执行以下操作
...
AuditLog logEntry = new AuditLog();
// set all the values except generated ones like id, created_at and updated_at
logEntry.setMethodName(...);
logEntry.setParam1(...);
logEntry.setRequest(...);
// Save into the table using autowired repoitory
auditLogRepoitory.saveAndFlush(logEntry);
// ... do the operation of the API
// Update the logEntry
logEntry.setResult(...);
logEntry.setResponse(...);
auditLogRepoitory.saveAndFlush(logEntry);
...
请帮助我改进对表格的插入和更新。 或者请帮助改进代码,以便我可以更快地响应 API。
谢谢, 斯里普拉德
【问题讨论】:
那是多少个插入?磁盘:HDD 还是 SSD? 请提供生成的SQL。一方面,我无法跟上一百个向开发人员隐藏 MySQL 的框架。 【参考方案1】:如果您的框架允许,请这样做
START TRANSACTION
在构建页面和存储审计的开始。和
COMMIT
在最后。
【讨论】:
【参考方案2】:初步提示
如果您想加快插入/更新速度,请不要使用 JpaRepository.save 方法(注意 saveAndFlush() 内部调用 save 方法)。 因为 JpaRepository.save 内部选择实体是为了知道实体是新的还是存在于数据库中。 这是 jpaRepository.save 的默认实现:
@Transactional
public <S extends T> S save(S entity)
Assert.notNull(entity, "Entity must not be null.");
if (this.entityInformation.isNew(entity))
this.em.persist(entity);
return entity;
else
return this.em.merge(entity);
我认为使用 jdbcTemplate 是最好的选择。
第二个提示
在考虑优化插入时,考虑进行批量插入可能很有用。根据mysql documentation website,插入一行所需的时间由以下因素决定,其中数字表示大致比例:
-
连接:(3)
向服务器发送查询:(2)
解析查询:(2)
插入行:(1 × 行大小)
插入索引:(1 × 索引数)
结束:(1)
因此您可以轻松了解批量插入如何帮助您提高插入速度。
第三个技巧
您可能需要按照stackeroverflow anwser 中的说明调整 mysql 实例设置
其他选项
确保您选择了正确的 ID 生成策略,如此处https://dzone.com/articles/spring-boot-boost-jpa-bulk-insert-performance-by-100x 所述
【讨论】:
感谢您的宝贵时间。但是我想我的问题还没有解决。 1. 关于 JPA 存储库,我可以使用选择查询,因为它几乎不需要几毫秒。 2. 我的特定实现是一行一行地插入。我不确定它如何进行批量插入。例如:API 请求可以在不同时间出现,也可以同时出现多个请求。您能帮我展示在这种情况下如何进行批量插入吗? Mysql 实例设置是我可以探索的。让我做吧 考虑插入批处理的更简单方法:INSERTing
一次 100 行是 100 单行 INSERTs
的 10 倍。 (我怀疑 100 倍的说法。)
如果真的需要,如果性能对你来说真的很重要,你可能应该改变你的方法。您可以在内存中保留一定数量的日志(在 List 等 java 集合中)并一次保存此 List,而不是通过单独的 saveAndFlush 来保存日志。或类似的想法以上是关于如何更快地将单个行插入 MySQL 表?的主要内容,如果未能解决你的问题,请参考以下文章