Перейти к основному содержимому

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

· 9 мин. чтения

Задача: Сумма двух чисел

Напишите функцию twoSum. Которая получает массив целых чисел nums и целую сумму target, а возвращает индексы двух чисел, сумма которых равна target. Любой набор входных данных имеет ровно одно решение, и вы не можете использовать один и тот же элемент дважды. Ответ можно возвращать в любом порядке...

ANDROMEDA

1. Введение

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

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

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

2. ТараканДБ

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

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

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

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

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

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

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

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

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. Для каждого узла в локальном кластере требуются уникальные значения store , port и http-port .

При настройке распределенного кластера 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 :

./df53e8c34839103df736f50966ac7569.png

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

  • Cluster Health — основные показатели работоспособности кластера.
  • Метрики времени выполнения — метрики о количестве узлов, времени ЦП и использовании памяти.
  • Производительность SQL — показатели SQL-соединений, запросов и транзакций .
  • Сведения о репликации — показатели того, как данные реплицируются в кластере.
  • Node Details — подробная информация о действующих, мертвых и выведенных из эксплуатации узлах.
  • Сведения о базе данных — сведения о системных и пользовательских базах данных в кластере.

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. Создание таблицы

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

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

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", "foreach"
);
articleRepository.insertArticle(article);

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

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

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

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

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

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