【发布时间】:2018-06-14 15:56:56
【问题描述】:
在 Google Cloud Env 中使用 Spring Boot 和 Spanner。我们现在正在努力解决性能问题。 为了演示我设置了一个小型演示案例,以了解我们如何从 spanner 中检索数据的不同方法。
第一种方法
使用来自 Google 的“本地”驱动程序来实例化 dbClient 并像这样检索数据。
@Repository
public class SpannerNativeDAO implements CustomerDAO {
private final DatabaseClient dbClient;
private final String SQL = "select * from customer where customer_id = ";
public SpannerNativeDAO(
@Value("${spring.cloud.gcp.spanner.instanceId}") String instanceId,
@Value("${spring.cloud.gcp.spanner.database}") String dbId,
@Value("${spring.cloud.gcp.spanner.project-id}") String projectId,
@Value("${google.application.credentials}") String pathToCredentials)
throws IOException {
try (FileInputStream google_application_credentials = new FileInputStream(pathToCredentials)) {
final SpannerOptions spannerOptions =
SpannerOptions.newBuilder().setProjectId(projectId)
.setCredentials(ServiceAccountCredentials.fromStream(google_application_credentials)).build();
final Spanner spanner = spannerOptions.getService();
final DatabaseId databaseId1 = DatabaseId.of(projectId, instanceId, dbId);
dbClient = spanner.getDatabaseClient(databaseId1);
// give it a first shot to speed up consecutive calls
dbClient.singleUse().executeQuery(Statement.of("select 1 from customer"));
}
}
private Customer readCustomerFromSpanner(Long customerId) {
try {
Statement statement = Statement.of(SQL + customerId);
ResultSet resultSet = dbClient.singleUse().executeQuery(statement);
while (resultSet.next()) {
return Customer.builder()
.customerId(resultSet.getLong("customer_id"))
.customerStatus(CustomerStatus.valueOf(resultSet.getString("status")))
.updateTimestamp(Timestamp.from(Instant.now())).build();
}
} catch (Exception ex) {
//log
}
return null;
}
....
}
第二种方法
使用 Spring Boot 数据启动器 (https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-starters/spring-cloud-gcp-starter-data-spanner)
然后就是这样
@Repository
public interface SpannerCustomerRepository extends SpannerRepository<Customer, Long> {
@Query("SELECT customer.customer_id, customer.status, customer.status_info, customer.update_timestamp "
+ "FROM customer customer WHERE customer.customer_id = @arg1")
List<Customer> findByCustomerId(@Param("arg1") Long customerId);
}
现在,如果我采用第一种方法,与 Spanner 建立初始 gRPC 连接需要 > 5 秒,并且所有连续调用都在 1 秒左右。第二种方法只需要大约。 400 毫秒 为初始呼叫后的每个呼叫。 为了测试差异,我在一个 Spring Boot 项目中连接了两种解决方案,并将其与内存解决方案 (~100ms) 进行了比较。 所有给定的时间都指的是开发机器上的本地测试,但会返回到调查云环境中的性能问题。
我测试了几个不同的 SpannerOptions (SessionOptions) 没有结果,并在项目上运行了一个分析器。 我似乎 96% 的响应时间来自于建立到 spanner 的 gRPC 通道,而数据库本身在 5 毫秒内处理和响应。
我们真的不理解这种行为。我们只使用非常少的测试数据和几个小表格。
- DatabaseClient 应该管理 ConnectionPool,它本身被连接到 Singleton-Scoped Repository-Bean。所以会话应该被重用,对吗?
- 为什么第一种方法比第二种方法花费的时间长得多。 Spring FW 本身只是使用 DatabaseClient 作为 SpannerOperations / SpannerTemplate 中的成员。
- 我们一般如何才能减少延迟。每个 db 调用的简单响应超过 200 毫秒似乎是我们预期的四倍。 (我知道本地时间基准需要谨慎对待)
【问题讨论】:
-
gRPC 连接延迟如何测量?
-
我正在运行一个 bash 脚本 > 在循环中抛出 curl 语句并测量从接收调用到发送响应的平均响应时间。
标签: java spring-boot google-cloud-platform google-cloud-spanner grpc-java