«1. Введение

Это руководство является вводным руководством по использованию CockroachDB с Java.

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

Давайте начнем с определения того, что это такое.

2. CockroachDB

CockroachDB — это распределенная база данных SQL, построенная поверх транзакционного и согласованного хранилища ключей и значений.

Написанный на Go и с полностью открытым исходным кодом, его основными целями разработки являются поддержка транзакций ACID, горизонтальная масштабируемость и живучесть. С этими целями проектирования он стремится выдержать все, от сбоя одного диска до сбоя всего центра обработки данных с минимальной задержкой и без ручного вмешательства.

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

2.1. Ключевые возможности

Давайте продолжим изучение некоторых ключевых аспектов CockroachDB:

    Совместимость с SQL API и PostgreSQL — для структурирования, обработки и запроса данных Транзакции ACID — поддержка распределенных транзакций и обеспечение строгой согласованности Готовность к использованию в облаке — – разработано для работы в облаке или в локальном решении, обеспечивающем простую миграцию между различными поставщиками облачных услуг без перерыва в обслуживании. Горизонтальное масштабирование – добавление емкости так же просто, как указание нового узла в работающем кластере с минимальными затратами на оператора Репликация — реплицирует данные для обеспечения доступности и гарантирует согласованность между репликами. Автоматическое восстановление — бесперебойная работа до тех пор, пока большинство реплик остаются доступными для краткосрочных сбоев, а для более долгосрочных сбоев выполняется автоматическая перебалансировка реплик от отсутствующих узлов с использованием незатронутые реплики в качестве источников

3. Настройка CockroachDB

После того, как мы установили CockroachDB, мы можем запустить первый узел o для нашего локального кластера:

cockroach start --insecure --host=localhost;

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

На данный момент наш локальный кластер запущен и работает. Имея только один единственный узел, мы уже можем подключиться к нему и работать, но чтобы лучше использовать преимущества автоматической репликации, перебалансировки и отказоустойчивости CockroachDB, мы добавим еще два узла:

cockroach start --insecure --store=node2 \
  --host=localhost --port=26258 --http-port=8081 \
  --join=localhost:26257;

cockroach start --insecure --store=node3 \
  --host=localhost --port=26259 --http-port=8082 \
  --join=localhost:26257;

Для двух дополнительных узлов, мы использовали флаг присоединения для подключения новых узлов к кластеру, указав адрес и порт первого узла, в нашем случае localhost:26257. Для каждого узла в локальном кластере требуются уникальные значения хранилища, порта и http-порта.

При настройке распределенного кластера CockroachDB каждый узел будет находиться на отдельной машине, поэтому можно не указывать порт, хранилище и http-порт, поскольку достаточно значений по умолчанию. Кроме того, при присоединении дополнительных узлов к кластеру следует использовать фактический IP-адрес первого узла.

3.1. Настройка базы данных и пользователя

После того, как наш кластер будет запущен и запущен через консоль SQL, поставляемую с CockroachDB, нам нужно создать нашу базу данных и пользователя.

Прежде всего, давайте запустим консоль SQL:

cockroach sql --insecure;

Теперь давайте создадим нашу базу данных testdb, создадим пользователя и добавим гранты пользователю, чтобы иметь возможность выполнять операции CRUD:

CREATE DATABASE testdb;
CREATE USER user17 with password 'qwerty';
GRANT ALL ON DATABASE testdb TO user17;

~ ~~ Если мы хотим убедиться, что база данных была создана правильно, мы можем перечислить все базы данных, созданные в текущем узле:

SHOW DATABASES;

Наконец, если мы хотим проверить функцию автоматической репликации CockroachDB, мы можем проверить один из двух других узлов, если база данных была создана правильно. Для этого мы должны указать флаг порта при использовании консоли SQL:

cockroach sql --insecure --port=26258;

4. Мониторинг CockroachDB

Теперь, когда мы запустили наш локальный кластер и создали базу данных, мы можем отслеживать их, используя Интерфейс администратора CockroachDB:

«Этот пользовательский интерфейс администратора, который поставляется в комплекте с CockroachDB, доступен по адресу http://localhost:8080, как только кластер будет запущен и запущен. В частности, он предоставляет подробную информацию о конфигурации кластера и базы данных и помогает нам оптимизировать производительность кластера, отслеживая такие показатели, как:

    Cluster Health — основные показатели работоспособности кластера. Runtime Metrics — показатели количества узлов, процессорного времени, и использование памяти. Производительность SQL — метрики соединений, запросов и транзакций SQL. Сведения о репликации — метрики о том, как данные реплицируются в кластере. о системе и пользовательских базах данных в кластере

5. Настройка проекта

Учитывая наш работающий локальный кластер CockroachDB, чтобы иметь возможность подключиться к нему, мы должны добавить дополнительную зависимость в наш pom.xml:

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.1.4</version>
</dependency>

Или, для проекта Gradle:

compile 'org.postgresql:postgresql:42.1.4'

6. Использование CockroachDB

Теперь, когда понятно, с чем мы работаем и что все настроено правильно, давайте начнем его использовать.

Благодаря совместимости с PostgreSQL можно либо напрямую подключаться к JDBC, либо использовать ORM, например Hibernate (на момент написания (январь 2018 г.) оба драйвера были достаточно протестированы, чтобы претендовать на поддержку бета-уровня в соответствии с разработчики). В нашем случае мы будем использовать JDBC для взаимодействия с базой данных.

Для простоты мы рассмотрим основные операции CRUD, поскольку с них лучше всего начинать.

Начнем с подключения к базе данных.

6.1. Подключение к CockroachDB

Чтобы открыть соединение с базой данных, мы можем использовать метод getConnection() класса DriverManager. Для этого метода требуется строковый параметр URL-адреса подключения, имя пользователя и пароль:

Connection con = DriverManager.getConnection(
  "jdbc:postgresql://localhost:26257/testdb", "user17", "qwerty"
);

6.2. Создание таблицы

При работающем соединении мы можем начать создавать таблицу статей, которую будем использовать для всех операций CRUD:

String TABLE_NAME = "articles";
StringBuilder sb = new StringBuilder("CREATE TABLE IF NOT EXISTS ")
  .append(TABLE_NAME)
  .append("(id uuid PRIMARY KEY, ")
  .append("title string,")
  .append("author string)");

String query = sb.toString();
Statement stmt = connection.createStatement();
stmt.execute(query);

Если мы хотим убедиться, что таблица создана правильно, мы можем используйте команду SHOW TABLES:

PreparedStatement preparedStatement = con.prepareStatement("SHOW TABLES");
ResultSet resultSet = preparedStatement.executeQuery();
List tables = new ArrayList<>();
while (resultSet.next()) {
    tables.add(resultSet.getString("Table"));
}

assertTrue(tables.stream().anyMatch(t -> t.equals(TABLE_NAME)));

Давайте посмотрим, как можно изменить только что созданную таблицу.

6.3. Изменение таблицы

Если мы пропустили некоторые столбцы во время создания таблицы или потому, что они нам понадобились позже, мы можем легко добавить их:

StringBuilder sb = new StringBuilder("ALTER TABLE ").append(TABLE_NAME)
  .append(" ADD ")
  .append(columnName)
  .append(" ")
  .append(columnType);

String query = sb.toString();
Statement stmt = connection.createStatement();
stmt.execute(query);

После того, как мы изменили таблицу, мы можем проверить, был ли добавлен новый столбец с помощью команды SHOW COLUMNS FROM:

String query = "SHOW COLUMNS FROM " + TABLE_NAME;
PreparedStatement preparedStatement = con.prepareStatement(query);
ResultSet resultSet = preparedStatement.executeQuery();
List<String> columns = new ArrayList<>();
while (resultSet.next()) {
    columns.add(resultSet.getString("Field"));
}

assertTrue(columns.stream().anyMatch(c -> c.equals(columnName)));

6.4. Удаление таблицы

При работе с таблицами иногда нам нужно удалить их, и это легко сделать с помощью нескольких строк кода:

StringBuilder sb = new StringBuilder("DROP TABLE IF EXISTS ")
  .append(TABLE_NAME);

String query = sb.toString();
Statement stmt = connection.createStatement();
stmt.execute(query);

6.5. Вставка данных

После того, как мы определились с операциями, которые можно выполнять с таблицей, мы можем приступить к работе с данными. Мы можем приступить к определению класса Article:

public class Article {

    private UUID id;
    private String title;
    private String author;

    // standard constructor/getters/setters
}

Теперь мы можем увидеть, как добавить Article в нашу таблицу article:

StringBuilder sb = new StringBuilder("INSERT INTO ").append(TABLE_NAME)
  .append("(id, title, author) ")
  .append("VALUES (?,?,?)");

String query = sb.toString();
PreparedStatement preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(1, article.getId().toString());
preparedStatement.setString(2, article.getTitle());
preparedStatement.setString(3, article.getAuthor());
preparedStatement.execute();

6.6. Чтение данных

После того, как данные сохранены в таблице, мы хотим прочитать эти данные, и это может быть легко достигнуто:

StringBuilder sb = new StringBuilder("SELECT * FROM ")
  .append(TABLE_NAME);

String query = sb.toString();
PreparedStatement preparedStatement = connection.prepareStatement(query);
ResultSet rs = preparedStatement.executeQuery();

Однако, если мы не хотим читать все данные внутри таблицы article но только одна статья, мы можем просто изменить способ построения нашего PreparedStatement:

StringBuilder sb = new StringBuilder("SELECT * FROM ").append(TABLE_NAME)
  .append(" WHERE title = ?");

String query = sb.toString();
PreparedStatement preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(1, title);
ResultSet rs = preparedStatement.executeQuery();

6.7. Удаление данных

И последнее, но не менее важное: если мы хотим удалить данные из нашей таблицы, мы можем удалить ограниченный набор записей, используя стандартную команду DELETE FROM:

StringBuilder sb = new StringBuilder("DELETE FROM ").append(TABLE_NAME)
  .append(" WHERE title = ?");

String query = sb.toString();
PreparedStatement preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(1, title);
preparedStatement.execute();

Или мы можем удалить всю запись в пределах таблица с функцией TRUNCATE:

StringBuilder sb = new StringBuilder("TRUNCATE TABLE ")
  .append(TABLE_NAME);

String query = sb.toString();
Statement stmt = connection.createStatement();
stmt.execute(query);

6.8. Обработка транзакций

После подключения к базе данных по умолчанию каждый отдельный оператор SQL рассматривается как транзакция и автоматически фиксируется сразу после завершения его выполнения.

Однако, если мы хотим, чтобы два или более оператора SQL были сгруппированы в одну транзакцию, мы должны управлять транзакцией программно.

Во-первых, нам нужно отключить режим автоматической фиксации, установив для свойства autoCommit Connection значение false, а затем использовать методы commit() и rollback() для управления транзакцией.

Давайте посмотрим, как мы можем добиться согласованности данных при выполнении нескольких вставок:

try {
    con.setAutoCommit(false);

    UUID articleId = UUID.randomUUID();

    Article article = new Article(
      articleId, "Guide to CockroachDB in Java", "baeldung"
    );
    articleRepository.insertArticle(article);

    article = new Article(
      articleId, "A Guide to MongoDB with Java", "baeldung"
    );
    articleRepository.insertArticle(article); // Exception

    con.commit();
} catch (Exception e) {
    con.rollback();
} finally {
    con.setAutoCommit(true);
}

«

«В этом случае при второй вставке возникло исключение из-за нарушения ограничения первичного ключа, и поэтому в таблицу статей не было вставлено ни одной статьи.

7. Заключение

В этой статье мы объяснили, что такое CockroachDB, как настроить простой локальный кластер и как мы можем взаимодействовать с ним из Java.