Миграция из CouchBase в Azure Cosmos DB для NoSQL
ОБЛАСТЬ ПРИМЕНЕНИЯ: NoSQL
Azure Cosmos DB — это масштабируемая, глобально распределенная, полностью управляемая база данных. Она обеспечивает гарантированный доступ к данным с низкой задержкой. Дополнительные сведения об Azure Cosmos DB см. в этой обзорной статье. В этой статье приведены инструкции по переносу приложений Java, подключенных к Couchbase, к учетной записи API для NoSQL в Azure Cosmos DB.
Различия в номенклатуре
Ниже приведены основные функции, которые по-разному работают в Azure Cosmos DB и Couchbase.
Couchbase | Azure Cosmos DB |
---|---|
Сервер Couchbase | Учетная запись |
Bucket | База данных |
Bucket | Контейнер или коллекция |
Документ 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" } ] }
Поддержка Java SDK
Azure Cosmos DB имеет следующие пакеты средств разработки программного обеспечения (SDK) для поддержки различных платформ Java:
- Async SDK;
- Spring Boot SDK.
В разделах ниже описано использование каждого из этих пакетов SDK. Рассмотрим пример, в котором у нас есть три типа рабочих нагрузок.
Couchbase в качестве репозитория документов и настраиваемые запросы на основе данных Spring
Если рабочая нагрузка, которую вы переносите, основана на пакете SDK Spring Boot Based, можно выполнить следующие действия.
Добавьте родительский элемент в файл POM.xml:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.5.RELEASE</version> <relativePath/> </parent>
Добавьте свойства в файл POM.xml:
<azure.version>2.1.6</azure.version>
Добавьте зависимости в файл POM.xml:
<dependency> <groupId>com.microsoft.azure</groupId> <artifactId>azure-cosmosdb-spring-boot-starter</artifactId> <version>2.1.6</version> </dependency>
Добавьте свойства приложения в раздел ресурсов и укажите следующие параметры. Обязательно замените параметры URL-адреса, ключа и имени базы данных:
azure.cosmosdb.uri=<your-cosmosDB-URL> azure.cosmosdb.key=<your-cosmosDB-key> azure.cosmosdb.database=<your-cosmosDB-dbName>
Определите имя коллекции в модели. Можно также указать дополнительные заметки. Например, можно явно задать идентификатор и ключ секции:
@Document(collection = "mycollection") public class User { @id private String id; private String firstName; @PartitionKey private String lastName; }
Ниже приведены фрагменты кода для операций CRUD.
Операции вставки и обновления
Где _repo — объект репозитория, а doc — объект класса POJO. Метод .save
можно использовать для вставки или операции upsert (если найден документ с указанным идентификатором). В следующем фрагменте кода показано, как вставить или обновить объект doc:
_repo.save(doc);
Операция удаления
Ниже приведен фрагмент кода, в котором объект doc будет иметь идентификатор и ключ секции, обязательные для нахождение и удаления объекта:
_repo.delete(doc);
Операция чтения
Документ можно прочитать с указанием ключа секции или без него. Если ключ секции не указан, он рассматривается как межсекционный запрос. Ниже приведены примеры кода, первый из которых будет выполнять операцию с использованием идентификатора и поля ключа секции. Во втором примере используется обычное поле и не указывается поле ключа секции.
_repo.findByIdAndName(objDoc.getId(),objDoc.getName());
_repo.findAllByStatus(objDoc.getStatus());
Теперь вы можете использовать свое приложение с Azure Cosmos DB. Полный код примера, описанного в этом документе, доступен в репозитории GitHub под названием CouchbaseToCosmosDB-SpringCosmos.
Couchbase в качестве репозитория документов и запросы N1QL
Запросы N1QL — это способ определения запросов в Couchbase.
Запрос N1QL | Запрос Azure Cosmos DB |
---|---|
SELECT META(TravelDocument ).id AS id, TravelDocument .* FROM TravelDocument WHERE _type = "com.xx.xx.xx.xxx.xxx.xxxx " and country = 'India’ and ANY m in Visas SATISFIES m.type == 'Multi-Entry' and m.Country IN ['India', Bhutan’] 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.xx.xxx.xxx.xxxx" and c.country = 'India' and m.type = 'Multi-Entry' and m.Country IN ('India', 'Bhutan') 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. Вместо него гораздо эффективнее будет использовать пакет Async Java SDK.
Операция чтения
Чтобы использовать Async Java SDK, выполнив указанные ниже действия.
Настройте следующую зависимость в файле 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>
Создайте объект подключения для Azure Cosmos DB с помощью метода
ConnectionBuilder
, как показано в следующем примере. Поместите это объявление в класс (бин) таким образом, чтобы следующий код выполнялся только один раз: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);
Чтобы выполнить запрос, необходимо выполнить следующий фрагмент кода:
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 требуется указать документ, который необходимо обновить. Чтобы получить весь документ, можно использовать фрагмент, приведенный под заголовком "Операция чтения", а затем изменить нужные поля. Следующий фрагмент кода выполняет для документа операцию 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 см. в описании операции вставки. Полный пример кода доступен в репозитории GitHub под названием CouchbaseToCosmosDB-AsyncInSpring.
Couchbase в качестве пары "ключ-значение"
Это простой тип рабочей нагрузки, который допускает выполнение поиска вместо запросов. Для пар "ключ-значение" выполните указанные ниже действия.
Рекомендуется использовать "/ID" в качестве первичного ключа, что гарантирует возможность выполнения операции поиска непосредственно в определенной секции. Создайте коллекцию и укажите "/ID" в качестве ключа секции.
Полностью выключите индексирование. Так как вы будете выполнять операции поиска, нет смысла создавать дополнительную нагрузку за счет индексирования. Чтобы выключить индексирование, войдите на портал Azure и перейдите в учетную запись Azure Cosmos DB. Откройте обозреватель данных, выберите свою базу данных и контейнер. Откройте вкладку Масштаб и параметры и выберите политику индексирования. Текущая политика индексации выглядит следующим образом:
{ "indexingMode": "consistent", "automatic": true, "includedPaths": [ { "path": "/*" } ], "excludedPaths": [ { "path": "/\"_etag\"/?" } ] }
Замените указанную выше политику индексирования следующей:
{ "indexingMode": "none", "automatic": false, "includedPaths": [], "excludedPaths": [] }
Чтобы создать объект подключения, используйте следующий фрагмент кода. Объект соединения (который будет помещен в @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 см. в описании операции вставки. Полный пример кода доступен в репозитории GitHub под названием CouchbaseToCosmosDB-AsyncKeyValue.
Миграция данных
Используйте Фабрика данных Azure для переноса данных. Это наиболее предпочтительный способ переноса данных. Настройка источника в качестве Couchbase и приемника в качестве Azure Cosmos DB для NoSQL см. в статье соединителя Фабрики данных Azure Cosmos DB для подробных действий.
Next Steps
- Чтобы выполнить тестирование производительности, см. статью Тестирование производительности и масштабирования с помощью Azure Cosmos DB.
- Чтобы оптимизировать код, см. статью Советы по повышению производительности для Azure Cosmos DB.
- Ознакомьтесь с Java Async V3 SDK и справочником по пакету SDK в репозитории GitHub.