Bootiful GCP:使用 Spanner 实现全局一致数据访问 (3/8)

工程 | Josh Long | 2018年8月27日 | ...

各位 Spring 粉丝大家好!在这个简短的 8 部分系列中,我们将介绍 Spring Cloud 与 Google Cloud Platform 的集成,称为 Spring Cloud GCP。Spring Cloud GCP 代表了 Google 和 Pivotal 之间的联合努力,旨在为使用 Google Cloud Platform 的 Spring Cloud 开发者提供一流的体验。Pivotal Cloud Foundry 用户将享受 与 GCP 服务代理更轻松的集成。我撰写了这些部分,并得到了 Google Cloud 开发者布道师、我的好友 Ray Tsang 的宝贵意见。您也可以在我们的 Google Next 2018 会议 Bootiful Google Cloud Platform 中观看 Spring Cloud GCP 的详细介绍。谢谢我的朋友!一如既往,如果您有反馈,我很乐意听取您的意见

本系列共有八篇文章。以下是全部文章:

](https://springframework.org.cn/blog/2018/09/06/bootiful-gcp-supporting-observability-with-spring-cloud-gcp-stackdriver-trace-6-8)

  • [Bootiful GCP:使用 Spring Cloud GCP 连接到其他 GCP 服务 (7/8)

](https://springframework.org.cn/blog/2018/09/10/bootiful-gcp-use-spring-cloud-gcp-to-connect-to-other-gcp-services-7-8)

MySQL 和 PostgreSQL 在这片陌生的土地上是熟悉的朋友,但这并不是我们来这里的原因。不不。如果我是你,我会看看像 GCP 这样的平台,并从中汲取最好的部分;那些在别处找不到对应项的部分。那些将其与其他平台区分开来的东西。一个这样的突出特点是 Google Spanner。Spanner 完全是...另一种东西。在这一部分中,我们将看看 Google Cloud Spanner。

Google 首次披露 Spanner 是在他们推出 F1 时,F1 是一个 SQL 数据库引擎,Adwords 团队于 2012 年 MySQL(“但 Josh!”我仿佛听到你喊道,“我们不是刚部署到 MySQL 吗??”)转移到了它。Spanner 在全球范围内提供低延迟读取,以及在较小程度上提供写入。Google 在 2012 年的一篇研究论文 中公布了它,其中称 Spanner 是“第一个在全球规模上分发数据并支持外部一致分布式事务的系统”。

“Spanner 在分布式系统中最困难的问题之一——支持外部一致事务并在合理延迟范围内实现的全局复制数据库——上取得了令人印象深刻的成就,”Basho 的首席架构师 Andy Gross 表示

Spanner 能够提供如此广泛的地理冗余,这得益于 Google 开发的一种方法,该方法能够为应用程序提供精确的时间,使其能够读写和复制数据而不会出错。Spanner 的“TrueTime” API 依赖于安装在 Google 数据中心内的 GPS 接收器和原子钟,允许应用程序在本地获得准确的时间读数,而无需进行全球同步。

Google 有许多数据库技术,例如 Bigtable(列式数据库,非常适合高吞吐量写入)和 Megastore(NoSQL 数据库)。Bigtable 只支持跨数据中心的最终一致性复制。根据论文:“Google 内部至少有 300 个应用程序使用 Megastore(尽管其性能相对较低),因为其数据模型比 Bigtable 的更易于管理,并且支持跨数据中心的同步复制。”当时,Gmail、Picasa、Calendar、Android Market 和 AppEngine 等应用程序都依赖于 Megastore。

Spanner 设计为“可扩展、多版本、全球分布式和同步复制的数据库”。事务在 Spanner 中是一个一流的概念,部分是由于 Bigtable 中缺乏事务。

“Bigtable 缺乏跨行事务导致频繁的抱怨;Percolator 的部分构建就是为了解决这一不足。一些作者声称,由于其带来的性能或可用性问题,通用二阶段提交(two-phase commit)开销过高。我们认为,最好让应用程序程序员在瓶颈出现时处理由于过度使用事务导致的性能问题,而不是总是围绕着事务的缺乏进行编码。在 Paxos 之上运行二阶段提交缓解了可用性问题。”

每种数据库都有其用例。Bigtable(在 GCP 上称为 Cloud Bigtable)非常适合持续低延迟和高吞吐量的工作负载。而 Megastore(在 GCP 上称为 Cloud Datastore)可用作托管的 NoSQL 数据存储,并支持 ACID 事务。Spanner(在 GCP 上称为 Cloud Spanner)专为水平可伸缩、高可用且强一致的关系型数据库(RDBMs)工作负载而设计。

好吧!我既感兴趣又感到畏惧!我想要 Spanner,但我不亲自架设服务器并同步 GPS 接收器和原子钟。但是,有什么告诉我 Google 会乐意为我做这件事,所以我们来试试看。

像以前一样,在使用之前,您需要启用 Google Cloud GCP Spanner 的 API

gcloud services enable spanner.googleapis.com

然后,创建一个新的 Google Cloud Spanner 实例

gcloud spanner instances create reservations --config=regional-us-central1 \
  --nodes=1 --description="Reservations for everybody"

然后,创建数据库实例

gcloud spanner databases create reservations --instance=reservations

确认 Spanner 实例可用

gcloud spanner databases list --instance=reservations

一旦实例状态变为 READY,就可以创建表了。以下是 Spanner DDL。如果这看起来非常像 SQL,那很好!它就应该如此。将此 DDL 放入一个单独的文件中。我将其命名为 schema.ddl

schema.ddl

CREATE TABLE reservations (
  id        STRING (36) NOT NULL,
  name      STRING (255) NOT NULL
) PRIMARY KEY (id );

将 schema 注册到数据库。

gcloud spanner databases ddl update reservations \
  --instance=reservations --ddl="$(./gcp/src/main/resources/db/schema.ddl )"

现在我们可以在 Spring 应用程序中从 Spanner 读取数据了。自动配置需要一些配置才能与正确的数据库进行通信。

application.properties

spring.cloud.gcp.spanner.instance-id=reservations-demo
spring.cloud.gcp.spanner.database=reservations

我们将使用全新的 Spring Data Spanner 模块,它在处理 Spanner 时支持常见的 Spring Data 用法习惯。将 org.springframework.cloud : spring-cloud-gcp-starter-data-spanner 添加到您的 Maven 构建中。让我们使用一个 Spring Data repository 来轻松完成数据库读取操作。

package com.example.gcp.spanner;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.cloud.gcp.data.spanner.core.mapping.PrimaryKey;
import org.springframework.cloud.gcp.data.spanner.core.mapping.Table;
import org.springframework.context.event.EventListener;
import org.springframework.data.annotation.Id;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

import java.util.UUID;
import java.util.stream.Stream;

@Slf4j
@SpringBootApplication
public class SpannerApplication {

        private final ReservationRepository reservationRepository;

        SpannerApplication(ReservationRepository reservationRepository) {
                this.reservationRepository = reservationRepository;
        }

        @EventListener(ApplicationReadyEvent.class)
        public void setup() {

                
                this.reservationRepository.deleteAll();

                Stream
                    .of("josh", "ray")
                    .map(name -> new Reservation(UUID.randomUUID().toString(), name))
                    .forEach(this.reservationRepository::save);
                this.reservationRepository.findAll().forEach(r -> log.info(r.toString()));
        }

        public static void main(String args[]) {
                SpringApplication.run(SpannerApplication.class, args);
        }
}


@Data
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "reservations")
class Reservation {

        @Id
        @PrimaryKey
        private String id;
        private String name;
}


@RepositoryRestResource
interface ReservationRepository extends PagingAndSortingRepository<Reservation, String> {
}
  • 我们启动应用程序,删除现有数据,然后使用我们由 Spring Data Spanner 提供支持的 repository 向数据库写入一些新数据。

  • 我们使用自定义映射注解 @Table@PrimaryKey 定义 Spring Data Spanner 实体。

  • 我们创建一个 Spring Data repository,该 repository 也使用 Spring Data REST 作为 REST API 暴露。

如果您曾经使用过 Spring Data,这个示例应该看起来很熟悉。Spring Data Spanner 构建在熟悉的概览和模式之上——模板、repository 和实体——以支持熟悉的数据库访问模式,只是数据库类型非常不同。

获取 Spring 电子报

订阅 Spring 电子报,保持连接

订阅

领先一步

VMware 提供培训和认证,助力您的职业发展。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看全部