Поделиться через


Миграция из CouchBase в Azure Cosmos DB для NoSQL

Azure Cosmos DB — это масштабируемая, глобально распределенная, полностью управляемая база данных. Она обеспечивает гарантированный доступ к данным с низкой задержкой. Дополнительные сведения об Azure Cosmos DB см. в этой обзорной статье. В этой статье приведены инструкции по переносу приложений Java, подключенных к Couchbase, к учетной записи API для NoSQL в Azure Cosmos DB.

Различия в номенклатуре

Ниже приведены основные функции, которые работают по-разному в Azure Cosmos DB по сравнению с Couchbase:

Couchbase Azure Cosmos DB (облачная база данных)
Сервер Couchbase Счет
ведро База данных
ведро Контейнер или коллекция
Документ JSON Элемент / документ

Основные отличия

  • Azure Cosmos DB имеет поле "ID" в документе, в то время как Couchbase имеет идентификатор в составе контейнера. Поле "ID" уникально в разделе.

  • Azure Cosmos DB масштабируется с помощью метода секционирования или сегментирования. Это означает, что данные разделяются на несколько сегментов или секций. Эти разделы/шарды создаются на основе свойства ключа разделения, который вы предоставляете. Вы можете выбрать ключ раздела для оптимизации операций чтения, а также записи, или для оптимизации чтения и записи одновременно. Дополнительные сведения см. в статье о секционированиях .

  • В Azure Cosmos DB не требуется обозначать коллекцию на иерархическом верхнем уровне, так как имя коллекции уже существует. Эта функция упрощает структуру JSON. Ниже приведен пример, показывающий различия в модели данных между Couchbase и Azure Cosmos DB:

    Couchbase: Идентификатор документа = "99FF4444"

    {
      "TravelDocument":
      {
        "Country":"India",
        "Validity" : "2022-09-01",
        "Person":
        {
          "Name": "Manish",
          "Address": "AB Road, City-z"
        },
        "Visas":
        [
          {
          "Country":"India",
          "Type":"Multi-Entry",
          "Validity":"2022-09-01"
          },
          {
          "Country":"US",
          "Type":"Single-Entry",
          "Validity":"2022-08-01"
          }
        ]
      }
    }
    

    Azure Cosmos DB: Ссылайтесь на "ID" в документе, как показано ниже.

    {
      "id" : "99FF4444",
    
      "Country":"India",
       "Validity" : "2022-09-01",
        "Person":
        {
          "Name": "Manish",
          "Address": "AB Road, City-z"
        },
        "Visas":
        [
          {
          "Country":"India",
          "Type":"Multi-Entry",
          "Validity":"2022-09-01"
          },
          {
          "Country":"US",
          "Type":"Single-Entry",
          "Validity":"2022-08-01"
          }
        ]
      }
    

Поддержка пакета SDK для Java

Azure Cosmos DB имеет следующие пакеты средств разработки программного обеспечения (SDK) для поддержки различных платформ Java:

  • Async SDK
  • Spring Boot SDK

В следующих разделах описывается, когда следует использовать каждый из этих пакетов SDK. Рассмотрим пример, в котором у нас есть три типа рабочих нагрузок:

Couchbase в качестве репозитория документов и пользовательских запросов на основе Spring Data

Если рабочая нагрузка, которую вы переносите, основана на пакете SDK Spring Boot Based, можно выполнить следующие действия.

  1. Добавьте родительский элемент в файл POM.xml:

    <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.1.5.RELEASE</version>
       <relativePath/>
    </parent>
    
  2. Добавьте свойства в файл POM.xml:

    <azure.version>2.1.6</azure.version>
    
  3. Добавьте зависимости в файл POM.xml:

    <dependency>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>azure-cosmosdb-spring-boot-starter</artifactId>
        <version>2.1.6</version>
    </dependency>
    
  4. Добавьте свойства приложения в ресурсы и укажите следующее. Обязательно замените параметры URL-адреса, ключа и имени базы данных:

       azure.cosmosdb.uri=<your-cosmosDB-URL>
       azure.cosmosdb.key=<your-cosmosDB-key>
       azure.cosmosdb.database=<your-cosmosDB-dbName>
    
  5. Определите имя коллекции в модели. Кроме того, можно указать дополнительные заметки. Например, ID, ключ раздела, для их явного обозначения:

    @Document(collection = "mycollection")
        public class User {
            @id
            private String id;
            private String firstName;
            @PartitionKey
            private String lastName;
        }
    

Ниже приведены фрагменты кода для операций CRUD:

Операции вставки и обновления

Где _repo является объектом репозитория и документом является объект класса POJO. Можно использовать .save для вставки или upsert (если документ с указанным идентификатором найден). В следующем фрагменте кода показано, как вставить или обновить объект документации:

_repo.save(doc);

Операция удаления

Рассмотрим следующий фрагмент кода, в котором объект doc будет иметь идентификатор и ключ секции, обязательный для поиска и удаления объекта:

_repo.delete(doc);

Операция чтения

Вы можете прочитать документ с указанием ключа раздела или без него. Если ключ секции не указан, он рассматривается как межсекционный запрос. Рассмотрим следующие примеры кода, сначала будет выполняться операция с помощью поля идентификатора и ключа секции. Во втором примере используется регулярное поле и без указания поля ключа раздела.

  • _repo.findByIdAndName(objDoc.getId(),objDoc.getName());
  • _repo.findAllByStatus(objDoc.getStatus());

Это так, теперь вы можете использовать приложение с Azure Cosmos DB. Полный пример кода, описанный в этом документе, доступен в репозитории CouchbaseToCosmosDB-SpringCosmos GitHub.

Couchbase в качестве репозитория документов и использование запросов N1QL

Запросы N1QL — это способ определения запросов в Couchbase.

Запрос N1QL Запрос Azure Cosmos DB
SELECT META(TravelDocument).id AS id, TravelDocument.* FROM TravelDocument WHERE _type = "com.xx.xx.xxxx" и страна = 'Индия' и ANY m в визах SATISFIES m.type == "Multi-Entry" и m.Country IN ['India', 'Бутан'] ORDER BY Validity DESC LIMIT 25 OFFSET 0 SELECT c.id,c FROM c JOIN m in c.country='India' WHERE c._type = " com.xx.xx.xxx.x" и c.country = "Индия" и m.type = "Multi-Entry" и m.Country IN ("Индия", "Бутан") ORDER BY c.Validity DESC OFFSET 0 LIMIT 25

Вы можете заметить следующие изменения в запросах N1QL:

  • Вам не нужно использовать ключевое слово META или ссылаться на документ первого уровня. Вместо этого можно создать собственную ссылку на контейнер. В этом примере мы рассмотрели его как "c" (это может быть что-нибудь). Эта ссылка используется в качестве префикса для всех полей первого уровня. Например, c.id, c.country и т. д.

  • Вместо "ANY" теперь можно выполнить объединение с поддокументом и ссылаться на него, используя выделенный псевдоним, например, "m". После создания псевдонима для поддокумента необходимо использовать псевдоним. Например, m.Country.

  • Последовательность OFFSET отличается в запросе Azure Cosmos DB, сначала необходимо указать OFFSET, а затем LIMIT. Рекомендуется не использовать пакет SDK Spring Data, если вы используете максимально настраиваемые запросы, так как это может иметь ненужные издержки на стороне клиента при передаче запроса в Azure Cosmos DB. Вместо этого у нас есть прямой пакет SDK для Async Java, который можно использовать гораздо эффективнее в этом случае.

Операция чтения

Используйте пакет SDK для Async Java, выполнив следующие действия.

  1. Настройте следующую зависимость в файле POM.xml:

    <!-- https://mvnrepository.com/artifact/com.microsoft.azure/azure-cosmosdb -->
    <dependency>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>azure-cosmos</artifactId>
        <version>3.0.0</version>
    </dependency>
    
  2. Создайте объект подключения для Azure Cosmos DB с помощью ConnectionBuilder метода, как показано в следующем примере. Убедитесь, что это объявление помещено в bean таким образом, чтобы следующий код выполнялся только один раз:

    ConnectionPolicy cp=new ConnectionPolicy();
    cp.connectionMode(ConnectionMode.DIRECT);
    
    if(client==null)
       client= CosmosClient.builder()
          .endpoint(Host)//(Host, PrimaryKey, dbName, collName).Builder()
           .connectionPolicy(cp)
           .key(PrimaryKey)
           .consistencyLevel(ConsistencyLevel.EVENTUAL)
           .build();
    
    container = client.getDatabase(_dbName).getContainer(_collName);
    
  3. Чтобы выполнить запрос, необходимо выполнить следующий фрагмент кода:

    Flux<FeedResponse<CosmosItemProperties>> objFlux= container.queryItems(query, fo);
    

Теперь с помощью приведенного выше метода можно передать несколько запросов и выполнить их без каких-либо затруднений. Если у вас есть требование выполнить один большой запрос, который можно разделить на несколько запросов, а затем попробуйте следующий фрагмент кода вместо предыдущего:

for(SqlQuerySpec query:queries)
{
   objFlux= container.queryItems(query, fo);
   objFlux .publishOn(Schedulers.elastic())
         .subscribe(feedResponse->
            {
               if(feedResponse.results().size()>0)
               {
                  _docs.addAll(feedResponse.results());
               }
            
            },
            Throwable::printStackTrace,latch::countDown);
   lstFlux.add(objFlux);
}
                  
      Flux.merge(lstFlux);
      latch.await();
}

С помощью предыдущего кода можно выполнять запросы параллельно и увеличивать распределенные выполнения для оптимизации. Кроме того, можно выполнить операции вставки и обновления:

Операция вставки

Чтобы вставить документ, выполните следующий код:

Mono<CosmosItemResponse> objMono= container.createItem(doc,ro);

Затем подпишитесь на Mono как:

CountDownLatch latch=new CountDownLatch(1);
objMono .subscribeOn(Schedulers.elastic())
        .subscribe(resourceResponse->
        {
           if(resourceResponse.statusCode()!=successStatus)
              {
                 throw new RuntimeException(resourceResponse.toString());
              }
           },
        Throwable::printStackTrace,latch::countDown);
latch.await();

Операция обновления/вставки (upsert)

Для операции upsert необходимо указать документ, который необходимо обновить. Чтобы получить полный документ, можно использовать фрагмент кода, упомянутый в разделе операции чтения заголовков, а затем изменить обязательные поля. Следующий фрагмент кода выполняет обновление или вставку для документа.

Mono<CosmosItemResponse> obs= container.upsertItem(doc, ro);

Затем подпишитесь на mono. Обратитесь к фрагменту подписки mono в операции вставки.

Операция удаления

Следующий фрагмент кода выполняет операцию удаления:

CosmosItem objItem= container.getItem(doc.Id, doc.Tenant);
Mono<CosmosItemResponse> objMono = objItem.delete(ro);

Затем подпишитесь на mono, см. фрагмент подписки mono в операции вставки данных. Полный пример кода доступен в репозитории CouchbaseToCosmosDB-AsyncInSpring GitHub.

Couchbase в качестве пары "ключ-значение"

Это простой тип рабочей нагрузки, в которой можно выполнять поиски вместо запросов. Выполните следующие действия для пар "ключ-значение".

  1. Рассмотрите возможность использования "/ID" в качестве первичного ключа, что гарантирует, что вы можете выполнять операцию поиска непосредственно в определенном разделе. Создайте коллекцию и укажите "/ID" в качестве ключа секции.

  2. Отключите индексирование полностью. Поскольку вы будете выполнять операции подстановки, нет смысла нести накладные расходы на индексирование. Чтобы отключить индексирование, войдите на портал Azure, перейдите к учетной записи Azure Cosmos DB. Откройте обозреватель данных, выберите базу данных и контейнер. Откройте вкладку "Масштаб и параметры" и выберите политику индексирования. В настоящее время политика индексирования выглядит следующим образом:

    {
     "indexingMode": "consistent",
     "automatic": true,
     "includedPaths": [
         {
             "path": "/*"
         }
     ],
     "excludedPaths": [
         {
             "path": "/\"_etag\"/?"
         }
     ]
     }
    

    Замените указанную выше политику индексирования следующим образом:

    {
     "indexingMode": "none",
     "automatic": false,
     "includedPaths": [],
     "excludedPaths": []
     }
    
  3. Чтобы создать объект подключения, используйте следующий фрагмент кода. Объект подключения (разместить в @Bean или сделать статическим):

    ConnectionPolicy cp=new ConnectionPolicy();
    cp.connectionMode(ConnectionMode.DIRECT);
    
    if(client==null)
       client= CosmosClient.builder()
          .endpoint(Host)//(Host, PrimaryKey, dbName, collName).Builder()
           .connectionPolicy(cp)
           .key(PrimaryKey)
           .consistencyLevel(ConsistencyLevel.EVENTUAL)
           .build();
    
    container = client.getDatabase(_dbName).getContainer(_collName);
    

Теперь можно выполнить операции CRUD следующим образом:

Операция чтения

Чтобы прочитать элемент, используйте следующий фрагмент кода:

CosmosItemRequestOptions ro=new CosmosItemRequestOptions();
ro.partitionKey(new PartitionKey(documentId));
CountDownLatch latch=new CountDownLatch(1);
      
var objCosmosItem= container.getItem(documentId, documentId);
Mono<CosmosItemResponse> objMono = objCosmosItem.read(ro);
objMono .subscribeOn(Schedulers.elastic())
        .subscribe(resourceResponse->
        {
           if(resourceResponse.item()!=null)
           {
              doc= resourceResponse.properties().toObject(UserModel.class);
           }
        },
        Throwable::printStackTrace,latch::countDown);
latch.await();

Операция вставки

Чтобы вставить элемент, можно выполнить следующий код:

Mono<CosmosItemResponse> objMono= container.createItem(doc,ro);

Затем подпишитесь на mono как:

CountDownLatch latch=new CountDownLatch(1);
objMono.subscribeOn(Schedulers.elastic())
      .subscribe(resourceResponse->
      {
         if(resourceResponse.statusCode()!=successStatus)
            {
               throw new RuntimeException(resourceResponse.toString());
            }
         },
      Throwable::printStackTrace,latch::countDown);
latch.await();

Операция обновления/вставки (upsert)

Чтобы обновить значение элемента, см. приведенный ниже фрагмент кода:

Mono<CosmosItemResponse> obs= container.upsertItem(doc, ro);

Затем подпишитесь на mono, см. фрагмент подписки mono в операции вставки данных.

Операция удаления

Используйте следующий фрагмент кода для выполнения операции удаления:

CosmosItem objItem= container.getItem(id, id);
Mono<CosmosItemResponse> objMono = objItem.delete(ro);

Затем подпишитесь на mono, см. фрагмент подписки mono в операции вставки данных. Полный пример кода доступен в репозитории CouchbaseToCosmosDB-AsyncKeyValue GitHub.

Миграция данных

Используйте фабрику данных Azure для переноса данных. Это наиболее рекомендуемый метод для переноса данных. Настройте источник как Couchbase и приемник как Azure Cosmos DB для NoSQL, см. статью соединитель Azure Cosmos DB Data Factory для подробных инструкций.

Дальнейшие шаги