Начало работы с Acapella DB

Введение

Что такое Acapella DB?

Это NewSQL база данных. От новых NoSQL баз данных она взяла масштабируемость и отказоустойчивость, а от классических – мощный язык запросов и ACID транзакции.

Acapella DB поддерживает распределённые транзакции, непривязанные к соединению: можно запустить транзакцию в одном месте, а продолжить из другого, указав её идентификатор. Уровень изоляции транзакций всегда SERIALIZABLE.

В этой статье описано начало работы с базой: подключение через SqlLine и JDBC, создание схемы и заполнение данных.

Когда нужна Acapella DB?

При работе с большими объёмами реляционных данных. Для реляционных данных небольшого объёма (до 1 ТБ) существуют классические SQL базы. Для плохо структурированных или слабо связанных данных используются NoSQL базы. 

NewSQL – единственный способ работы с большим количеством сложно взаимосвязанных консистентных данных.

При необходимости распределённых транзакций. У вас есть веб-сервис, который должен поддерживать расширения через web-hooks, но при этом нужно запускать их в одной транзакции с изначальным запросом. Распределённые транзакции Acapella DB позволяют это сделать.

Acapella DB уже можно пользоваться?

Да, на февраль 2019 года Acapella DB на раннем этапе развития: поддерживается не весь SQL, не полностью реализовано распределение данных по узлам, возможны баги. Но мы уже реализовали распределённые транзакции и реплицирование. Доступ к текущей версии можно получить по адресу db.acapella.ru:4567.

Начало работы

У Acapella DB ещё нет своего консольного клиента, но можно воспользоваться утилитой sqlline, которая позволяет подключиться к любой SQL базе через JDBC драйвер. SqlLine написан на Java, поэтому поддерживаются Windows, Linux и MacOS. Здесь приведены команды для Linux.

Скачаем sqlline, специально собранный для работы с Acapella DB: со всеми зависимостями и JDBC драйвером:

1
wget https://github.com/AcapellaSoft/adb-tools/releases/download/0.2.0/sqlline-0.2.0.jar -O sqlline.jar

Для запуска требуется Java Runtime Environment (JRE) версии 1.8 или выше.

Создадим пользователя. Его имя должно быть уникальным:

1
2
$ java -jar sqlline.jar -u jdbc:acapelladb://db.acapella.ru:4567
> CREATE USER <username> WITH PASSWORD '<password>';

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

Подключимся к базе данных и создадим простую таблицу:

1
2
3
$ java -jar sqlline.jar -u jdbc:acapelladb://db.acapella.ru:4567/<username> -n <username> -p <password>
> CREATE TABLE test("key" VARCHAR, "value" VARCHAR, PRIMARY KEY("key"));
No rows affected (2.337 seconds)

Первые запросы после изменения схемы могут быть долгими, не пугайтесь :). Созданная таблица с именем test содержит две колонки: key и value, обе строкового типа. Колонка key является основным ключом, по нему данные таблицы будут распределяться по узлам базы. Создать таблицу без основного ключа пока нельзя.

Создадим первую запись в таблице:

1
2
> INSERT INTO test VALUES ('hello', 'world');
1 ROW affected (0.045 seconds)

и получим её обратно:

1
2
3
4
5
6
7
> SELECT * FROM test;
+--------+--------+
| KEY    | VALUE  |
+--------+--------+
| hello  | world  |
+--------+--------+
1 ROW selected (0.343 seconds)

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

JDBC-клиент

Чтобы начать работу с JDBC, подключим adb-jdbc-driver к приложению:

Maven:

1
2
3
4
5
6
7
8
9
10
11
12
13
<repositories>
    <repository>
        <id>jcenter</id>
        <url>https://jcenter.bintray.com/</url>
    </repository>
</repositories>
...
<dependency>
    <groupId>ru.acapella.db</groupId>
    <artifactId>adb-jdbc-driver</artifactId>
    <version>0.2.0</version>
    <type>pom</type>
</dependency>

Gradle:

1
2
3
4
5
6
7
repositories {
  jcenter()
}
...
dependencies {
    compile 'ru.acapella.db:adb-jdbc-driver:0.2.0'
}

Создадим соединение:

1
2
3
4
5
6
7
8
// Kotlin
Class.forName("ru.acapella.db.jdbc.Driver")
val connection = DriverManager.getConnection(
  "jdbc:acapelladb://db.acapella.ru:4567/<username>",
  "<username>",
  "<password>"
)
connection.autoCommit = true
1
2
3
4
5
6
7
Class.forName("ru.acapella.db.jdbc.Driver");
Connection connection = DriverManager.getConnection(
  "jdbc:acapelladb://db.acapella.ru:4567/<username>",
  "<username>",
  "<password>"
);
connection.setAutoCommit(true);

Создадим и заполним таблицу:

1
2
3
4
// Kotlin
val statement = connection.createStatement()
statement.execute("""CREATE OR REPLACE TABLE test("key" VARCHAR, "value" VARCHAR, PRIMARY KEY("key"))""")
statement.execute("""INSERT INTO test VALUES ('hello', 'world')""")
1
2
3
4
// Java
Statement statement = connection.createStatement();
statement.execute("CREATE OR REPLACE TABLE test("key" VARCHAR, "value" VARCHAR, PRIMARY KEY("key"))");
statement.execute("INSERT INTO test VALUES ('hello', 'world')");

Выполним простую выборку:

1
2
3
4
5
6
7
// Kotlin
val resultSet = statement.executeQuery("""SELECT * FROM test""")
while (resultSet.next()) {
    val key = resultSet.getString(1)
    val value = resultSet.getString(2)
    println("$key, $value")
}
1
2
3
4
5
6
7
// Java
ResultSet resultSet = statement.executeQuery("SELECT * FROM test");
while (resultSet.next()) {
    String key = resultSet.getString(1);
    String value = resultSet.getString(2);
    System.out.printf("%s, %s", key, value);
}

JDBC клиент позволяет менять схему, обновлять и выбирать данные. Поддерживаются prepared statements, получение мета-информации о БД и основные типы данных. Уже сейчас можно запустить Hibernate поверх драйвера, чтобы работать с базой было удобнее.

«Real-world example»

Чтобы показать больше возможностей Acapella DB, создадим БД для работы с отелями, которую часто используют как пример работы с SQL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
CREATE TABLE city (
  zip VARCHAR,
  name VARCHAR,
  state VARCHAR,
  PRIMARY KEY(zip)
);

CREATE TABLE customer (
  id INTEGER,
  title VARCHAR,
  first_name VARCHAR,
  name VARCHAR,
  zip VARCHAR,
  address VARCHAR,
  PRIMARY KEY(id)
);

CREATE TABLE hotel (
  id INTEGER,
  name VARCHAR,
  zip VARCHAR,
  address VARCHAR,
  PRIMARY KEY(id)
);

CREATE INDEX hotel_by_name ON hotel (name);

CREATE TABLE room (
  hotel_id INTEGER,
  TYPE VARCHAR,
  "free" DECIMAL,
  price DECIMAL,
  PRIMARY KEY (hotel_id, TYPE)
);

CREATE TABLE reservation (
  id INTEGER,
  customer_id INTEGER,
  hotel_id INTEGER,
  TYPE VARCHAR,
  arrival DATE,
  departure DATE,
  PRIMARY KEY(id)
);

Acapella DB поддерживает основные типы данных: INTEGER, VARCHAR, DATE и др. Сейчас на создание таблиц действует ограничение: они должны иметь основной ключ. Поддерживаются PRIIMARY KEY с одной колонкой или с несколькими. Уже поддерживаются индексы, но пока нельзя создать уникальный индекс. Нет ограничений на вставку значения с уже существующим PRIMARY KEY, при этом произойдёт обновление записи.

Заполним таблицы данными:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
INSERT INTO city VALUES ('60615','Chicago','IL');
INSERT INTO city VALUES ('45211','Cincinnati','OH');
INSERT INTO city VALUES ('33575','Clearwater','FL');
INSERT INTO city VALUES ('75243','Dallas','TX');
INSERT INTO city VALUES ('32018','Daytona Beach','FL');
INSERT INTO city VALUES ('33441','Deerfield Beach','FL');
INSERT INTO city VALUES ('48226','Detroit','MI');
INSERT INTO city VALUES ('90029','Hollywood','CA');
INSERT INTO city VALUES ('92714','Irvine','CA');
INSERT INTO city VALUES ('90804','Long Beach','CA');
INSERT INTO city VALUES ('11788','Long Island','NY');
INSERT INTO city VALUES ('90018','Los Angeles','CA');
INSERT INTO city VALUES ('70112','New Orleans','LA');
INSERT INTO city VALUES ('10580','New York','NY');
INSERT INTO city VALUES ('10019','New York','NY');
INSERT INTO city VALUES ('92262','Palm Springs','CA');
INSERT INTO city VALUES ('97213','Portland','OR');
INSERT INTO city VALUES ('60018','Rosemont','IL');
INSERT INTO city VALUES ('95054','Santa Clara','CA');
INSERT INTO city VALUES ('20903','Silver Spring','MD');
INSERT INTO city VALUES ('20037','Seattle','WA');
INSERT INTO city VALUES ('20005','Seattle','WA');
INSERT INTO city VALUES ('20019','Seattle','WA');

INSERT INTO customer VALUES (3000,'Mrs','Jenny','Porter','10580','1340 N. Ash Street, #3');
INSERT INTO customer VALUES (3100,'Mr','Peter','Brown','48226','1001 34th St., APT.3');
INSERT INTO customer VALUES (3200,'Company',NULL,'Datasoft','90018','486 Maple St.');
INSERT INTO customer VALUES (3300,'Mrs','Rose','Brian','75243','500 Yellowstone Drive, #2');
INSERT INTO customer VALUES (3400,'Mrs','Mary','Griffith','20005','3401 Elder Lane');
INSERT INTO customer VALUES (3500,'Mr','Martin','Randolph','60615','340 MAIN STREET, #7');
INSERT INTO customer VALUES (3600,'Mrs','Sally','Smith','75243','250 Curtis Street');
INSERT INTO customer VALUES (3700,'Mr','Mike','Jackson','45211','133 BROADWAY APT. 1');
INSERT INTO customer VALUES (3800,'Mrs','Rita','Doe','97213','2000 Humboldt St., #6');
INSERT INTO customer VALUES (3900,'Mr','George','Howe','75243','111 B Parkway, #23');
INSERT INTO customer VALUES (4000,'Mr','Frank','Miller','95054','27 5th St., 76');
INSERT INTO customer VALUES (4100,'Mrs','Susan','Baker','90018','200 MAIN STREET, #94');
INSERT INTO customer VALUES (4200,'Mr','Joseph','Peters','92714','700 S. Ash St., APT.12');
INSERT INTO customer VALUES (4300,'Company',NULL,'TOOLware','20019','410 Mariposa St., #10');
INSERT INTO customer VALUES (4400,'Mr','Antony','Jenkins','20903','55 A Parkway, #15');

INSERT INTO hotel VALUES (10,'Congress','20005','155 Beechwood St.');
INSERT INTO hotel VALUES (30,'Regency','20037','477 17th Avenue');
INSERT INTO hotel VALUES (20,'Long Island','11788','1499 Grove Street');
INSERT INTO hotel VALUES (70,'Empire State','12203','65 Yellowstone Dr.');
INSERT INTO hotel VALUES (80,'Midtown','10019','12 Barnard St.');
INSERT INTO hotel VALUES (40,'Eighth Avenue','10019','112 8th Avenue');
INSERT INTO hotel VALUES (50,'Lake Michigan','60601','354 OAK Terrace');
INSERT INTO hotel VALUES (60,'Airport','60018','650 C Parkway');
INSERT INTO hotel VALUES (90,'Sunshine','33575','200 Yellowstone Dr.');
INSERT INTO hotel VALUES (100,'Beach','32018','1980 34th St.');
INSERT INTO hotel VALUES (110,'Atlantic','33441','111 78th St.');
INSERT INTO hotel VALUES (120,'Long Beach','90804','35 Broadway');
INSERT INTO hotel VALUES (150,'Indian Horse','92262','16 MAIN STREET');
INSERT INTO hotel VALUES (130,'Star','90029','13 Beechwood Place');
INSERT INTO hotel VALUES (140,'River Boat','70112','788 MAIN STREET');

INSERT INTO room VALUES (10,'single',20,135.00);
INSERT INTO room VALUES (10,'double',45,200.00);
INSERT INTO room VALUES (30,'single',12,45.00);
INSERT INTO room VALUES (30,'double',15,80.00);
INSERT INTO room VALUES (20,'single',10,70.00);
INSERT INTO room VALUES (20,'double',13,100.00);
INSERT INTO room VALUES (70,'single',4,115.00);
INSERT INTO room VALUES (70,'double',11,180.00);
INSERT INTO room VALUES (80,'single',15,90.00);
INSERT INTO room VALUES (80,'double',19,150.00);
INSERT INTO room VALUES (80,'suite',5,400.00);
INSERT INTO room VALUES (40,'single',20,85.00);
INSERT INTO room VALUES (40,'double',35,140.00);
INSERT INTO room VALUES (50,'single',50,105.00);
INSERT INTO room VALUES (50,'double',230,180.00);
INSERT INTO room VALUES (50,'suite',12,500.00);
INSERT INTO room VALUES (60,'single',10,120.00);
INSERT INTO room VALUES (60,'double',39,200.00);
INSERT INTO room VALUES (60,'suite',20,500.00);
INSERT INTO room VALUES (90,'single',45,90.00);
INSERT INTO room VALUES (90,'double',145,150.00);
INSERT INTO room VALUES (90,'suite',60,300.00);
INSERT INTO room VALUES (100,'single',11,60.00);
INSERT INTO room VALUES (100,'double',24,100.00);
INSERT INTO room VALUES (110,'single',2,70.00);
INSERT INTO room VALUES (110,'double',10,130.00);
INSERT INTO room VALUES (120,'single',34,80.00);
INSERT INTO room VALUES (120,'double',78,140.00);
INSERT INTO room VALUES (120,'suite',55,350.00);
INSERT INTO room VALUES (150,'single',44,100.00);
INSERT INTO room VALUES (150,'double',115,190.00);
INSERT INTO room VALUES (150,'suite',6,450.00);
INSERT INTO room VALUES (130,'single',89,160.00);
INSERT INTO room VALUES (130,'double',300,270.00);
INSERT INTO room VALUES (130,'suite',100,700.00);
INSERT INTO room VALUES (140,'single',10,125.00);
INSERT INTO room VALUES (140,'double',9,200.00);
INSERT INTO room VALUES (140,'suite',78,600.00);

INSERT INTO reservation VALUES (100,3000,80,'single',DATE'2004-11-13',DATE'2004-11-15');
INSERT INTO reservation VALUES (110,3000,100,'double',DATE'2004-12-24',DATE'2005-01-06');
INSERT INTO reservation VALUES (120,3200,50,'suite',DATE'2004-11-14',DATE'2004-11-18');
INSERT INTO reservation VALUES (130,3900,110,'single',DATE'2005-02-01',DATE'2005-02-03');
INSERT INTO reservation VALUES (150,3600,70,'double',DATE'2005-03-14',DATE'2005-03-24');
INSERT INTO reservation VALUES (140,4300,80,'double',DATE'2004-04-12',DATE'2004-04-30');
INSERT INTO reservation VALUES (160,4100,70,'single',DATE'2004-04-12',DATE'2004-04-15');
INSERT INTO reservation VALUES (170,4400,150,'suite',DATE'2004-09-01',DATE'2004-09-03');
INSERT INTO reservation VALUES (180,3100,120,'double',DATE'2004-12-23',DATE'2005-01-08');
INSERT INTO reservation VALUES (190,4300,140,'double',DATE'2004-11-14',DATE'2004-11-17');

Acapella DB поддерживает сложные условия выборок: JOIN, ORDER BY, агрегации, алиасы. Сделаем несколько выборок.

Выберем все отели в Нью-Йорке, отсортированные по названию:

1
2
3
4
5
SELECT h.name, h.id, h.address
FROM hotel h
JOIN city c ON h.zip = c.zip
WHERE c.name = 'New York'
ORDER BY h.name;

результат:

+---------------+------------+----------------+
|     name      | id         | address        |
+---------------+------------+----------------+
| Eighth Avenue | 40         | 112 8th Avenue |
| Midtown       | 80         | 12 Barnard St. |
+---------------+------------+----------------+

Выберем среднюю цену за комнату:

1
SELECT AVG(price) AS "avg" FROM room;

результат:

+---------------------+
|        avg          |
+---------------------+
| 201.5789473684211   |
+---------------------+

Выберем количество свободных комнат по отелям, отсортируем отели по названию:

1
2
3
4
SELECT h.name, SUM(r."free") AS "sum"
FROM hotel h
JOIN room r ON h.id = r.hotel_id
GROUP BY h.name;

результат:

+---------------+---------------------+
|     name      | sum                 |
+---------------+---------------------+
| Congress      | 65                  |
| Regency       | 27                  |
| Indian Horse  | 165                 |
| Long Island   | 23                  |
| Lake Michigan | 292                 |
| Sunshine      | 250                 |
| Eighth Avenue | 55                  |
| Beach         | 35                  |
| Star          | 489                 |
| Long Beach    | 167                 |
| River Boat    | 97                  |
| Airport       | 69                  |
| Empire State  | 15                  |
| Atlantic      | 12                  |
| Midtown       | 39                  |
+---------------+---------------------+

Заключение

Мы рассмотрели Acapella DB со стороны классических SQL баз данных: подключились через JDBC, создали реляционную схему данных, выполнили запросы с использованием джоинов и агрегаций. Но мы ещё далеки от уровня MySQL или PostgreSQL. Поддержка SQL позволяет попробовать Acapella DB без серьёзных изменений в стеке технологий. 

Мы рассказали только про SQL, но в Acapella DB есть ещё много интересного: распределение данных по узлам, поддержка консистентности в распределённых транзакциях.

Подробнее об этом мы расскажем в следующих статьях, которые будут в ближайшее время.

0 ответы

Ответить

Want to join the discussion?
Feel free to contribute!

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *