Руководство по CockroachDB на Java

Руководство по CockroachDB на Java

1. Вступление

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

Мы объясним основные функции, как настроить локальный кластер и как его контролировать,along with a practical guide on how we can use Java to connect and interact with the server.

Начнем с определения, что это такое.

2. ТараканDB

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

Написано на Go и полностью с открытым исходным кодом,its primary design goals are the support for ACID transactions, horizontal scalability, and survivability. С этими целями проектирования он стремится выдержать все, от отказа одного диска до отказа всего центра обработки данных с минимальными задержками и без ручного вмешательства.

В результатеCockroachDB can be considered a well-suited solution for applications that require reliable, available, and correct data regardless of scale. Однако это не лучший выбор, когда критически важны операции чтения и записи с очень малой задержкой.

2.1. Ключевая особенность

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

  • SQL API and PostgreSQL compatibility - для структурирования, манипулирования и запроса данных

  • ACID transactions - поддерживает распределенные транзакции и обеспечивает высокую согласованность

  • Cloud-ready - предназначен для работы в облаке или в локальном решении, обеспечивая легкую миграцию между различными поставщиками облачных услуг без прерывания обслуживания

  • Scales horizontally - добавить емкость так же просто, как указать новый узел в работающем кластере с минимальными накладными расходами оператора

  • Replication - реплицирует данные для доступности и гарантирует согласованность между репликами

  • Automated repair - непрерывно продолжать работу, пока большинство реплик остаются доступными для краткосрочных сбоев, в то время как в случае долговременных сбоев автоматически балансируются реплики с отсутствующих узлов, используя незатронутые реплики в качестве источников

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

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

cockroach start --insecure --host=localhost;

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

На данный момент наш локальный кластер запущен и работает. Имея только один единственный узел, мы уже можем подключиться к нему и работать, ноto better take advantages of CockroachDB’s automatic replication, rebalancing, and fault tolerance, we’ll add two more nodes:

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;

Для двух дополнительных узлов мы использовали флагjoin для подключения новых узлов к кластеру, указав адрес и порт первого узла, в нашем случае localhost: 26257. Each node on the local cluster requires unique store, port, and http-port values.

При настройке распределенного кластера CockroachDB каждый узел будет находиться на другом компьютере, поэтому указанияport,store иhttp-port можно избежать, поскольку значений по умолчанию достаточно. Кроме того, фактический 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, мы можем проверить на одном из двух других узлов, правильно ли была создана база данных. Для этого мы должны указать флагport, когда мы используем консоль SQL:

cockroach sql --insecure --port=26258;

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

Теперь, когда мы запустили локальный кластер и создали базу данныхwe can monitor them using the CockroachDB Admin UI:

image

К этому пользовательскому интерфейсу администратора, который поставляется в комплекте с CockroachDB, можно получить доступ черезhttp://localhost:8080, как только кластер будет запущен и запущен. В частности,it provides details about cluster and database configuration, and helps us optimize cluster performance by monitoring metrics like:

  • Cluster Health - основные показатели состояния кластера

  • Runtime Metrics - метрики о количестве узлов, времени ЦП и использовании памяти

  • SQL Performance - метрики о соединениях, запросах и транзакциях SQL

  • Replication Details - метрики о том, как данные реплицируются в кластере

  • Node Details - подробная информация о действующих, мертвых и списанных узлах

  • Database Details - подробная информация о системе и пользовательских базах данных в кластере

5. Project Setupс

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


    org.postgresql
    postgresql
    42.1.4

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

compile 'org.postgresql:postgresql:42.1.4'

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

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

Благодаря совместимости с PostgreSQL,it’s either possible to connect directly with JDBC or using an ORM, such as Hibernate (на момент написания (январь 2018 г.) оба драйвера были протестированы достаточно, чтобы заявить о поддержкеbeta-level по словам разработчиков). В нашем случае мы будем использовать JDBC для взаимодействия с базой данных.

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

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

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

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

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

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

При рабочем подключении мы можем начать создание таблицыarticles, которую мы собираемся использовать для всех операций 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 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 в нашу таблицуarticles:

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();

Однако, если мы хотим читать не все данные внутри таблицыarticles, а только один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 в одну транзакцию, мы должны программно контролировать транзакцию.

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

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

try {
    con.setAutoCommit(false);

    UUID articleId = UUID.randomUUID();

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

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

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

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

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

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

Полный исходный код этой статьи, как всегда, можно найти вover on Github.