В первой части этой серии руководств я рассмотрел основы Cassandra и использовал CQLSH для взаимодействия с системой баз данных через оболочку. Во второй части я кратко расскажу об основных типах данных, доступных в CQL. Затем я расскажу об gocql , клиентского пакета Golang, который реализует драйвер Cassandra для Golang. Я расскажу, как создать сеансовое соединение с Cassandra с некоторыми параметрами конфигурации, а затем как выполнять различные запросы с использованием сеанса.
Cassandra обеспечивает поддержку основных типов данных, которые доступны практически во всех системах баз данных. Помимо этого, он также предусматривает сложные типы коллекций, которые могут хранить комбинации простых данных в форме списка, набора и карты. Помимо этого, в CQL также есть поддержка пользовательских типов, что позволяет разработчикам иметь свои собственные типы данных, которые легко читать и понимать.
Основные типы данных
- ascii: представляет строку символов ASCII. Вставка любого не-ASCII символа в столбец этого типа приведет к ошибке.
- bigint: представляет 64-битный подписанный длинный. Используется для хранения длинных номеров. Это следует использовать только тогда, когда мы уверены, что нам нужны такие длинные числа, потому что это занимает больше места по сравнению с int .
- blob: используется для хранения произвольных байтов. Это представляется в шестнадцатеричном формате, и любые данные без какой-либо проверки могут быть сохранены в этом поле.
- логическое: хранит
trueилиfalse.
- counter: представляет 64-разрядное целое число со знаком, но значение этого столбца установить нельзя. В этом столбце есть только две операции: увеличение и уменьшение. В таблице со столбцом счетчика разрешены только типы счетчиков и первичный ключ. В таблице со столбцами счетчиков не допускаются операторы
INSERT; можно использовать толькоUPDATE. Например:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
> CREATE TABLE website_tracker (
id int PRIMARY KEY,
url text,
visitor_count counter
);
> UPDATE website_tracker
SET visitor_count = visitor_count + 1
WHERE id = 1;
> SELECT * FROM website_tracker;
id |
—-+——+——
1 |
(1 rows)
|
- дата: представляет значение даты без значения времени. Кассандра кодирует то же самое, что и целочисленное значение с эпохи. Даты могут быть представлены в виде строк в формате
yyyy-mm-dd.
- десятичное число: представляет десятичное значение переменной точности. Лучше всего для хранения валюты или финансовых ценностей.
- double: хранит 64-битное значение с плавающей точкой.
- float: хранит 32-битное значение с плавающей точкой.
- inet: представляет строку IP-адреса в формате IPv4 или IPv6.
- int: представляет 32-разрядное целое число со знаком. Используется в основном при хранении целочисленных значений.
- smallint: представляет 2-байтовое (16-битное) целое число. Может быть предпочтительнее, чем int для хранения небольших целочисленных значений для экономии места.
- text: представляет строку в кодировке UTF-8. Должен использоваться, когда мы хотим хранить не-ASCII символы.
- время: представляет значение времени. Представляется в виде строки в формате
01:02:03.123и01:02:03.12364-разрядное целое число со01:02:03.123представляющее наносекунды, прошедшие с полуночи.
- отметка времени: хранит компоненты даты и времени с точностью до миллисекунды. Может быть представлен в виде текста в формате
2016-12-01 01:02:03.123.
- tinyint: представляет 1-байтовое (8-битное) целое число. Может быть предпочтительнее, чем int или smallint, для хранения небольших целочисленных значений для экономии места.
- timeuuid: хранит UUID версии 1.
- UUID : UUID в стандартном формате. Это большее значение по сравнению с timeuuid.
- varchar: похоже на текст Оба могут быть использованы взаимозаменяемо.
- вариант: целочисленное значение с произвольной точностью. Рекомендуется использовать тип данных с необходимой точностью.
Типы сбора данных
- set: этот тип хранит коллекцию значений. Значения хранятся как неупорядоченные, но CQLSH вернет их отсортированным образом. Например, строки будут отсортированы в алфавитном порядке. Давайте изменим таблицу, которую мы создали выше:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
> ALTER TABLE website_tracker ADD tagsSet set<text>;
> UPDATE website_tracker SET tagsSet = {‘tag1’} WHERE id = 1;
> SELECT tagsSet FROM website_tracker WHERE id = 1;
tagsSet
———-
{‘tag1’}
> UPDATE website_tracker SET tagsSet = tagsSet + {‘gat2’} WHERE id = 1;
> SELECT tagsSet FROM website_tracker WHERE id = 1;
tagsSet
——————
{‘gat2’, ‘tag1’}
|
Вы можете использовать обычные операции над множествами, такие как difference для удаления элементов. Чтобы очистить или заменить полный набор, выполните SET tags = {<something>} .
- list: список также хранит коллекцию значений, но сохраняет их в упорядоченном порядке, который по умолчанию добавляется в порядке вставки. Давайте попробуем сделать то же самое, что мы сделали выше с наборами со списком:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
> ALTER TABLE website_tracker ADD tagsList list<text>;
> UPDATE website_tracker SET tagsList = [‘tag1’] WHERE id = 1;
> SELECT tagsList FROM website_tracker WHERE id = 1;
tagsList
———-
[‘tag1’]
> UPDATE website_tracker SET tagsList = tagsList + [‘gat2’] WHERE id = 1;
> SELECT tagsList FROM website_tracker WHERE id = 1;
tagsList
——————
[‘tag1’, ‘gat2’]
|
В списке значения могут быть добавлены, вычтены (как в наборах), вставлены / заменены / удалены по значению индекса ( SET tags[1] = '<somevalue>' ) и т. Д.
- карта: карта содержит коллекцию пар ключ-значение. Это может быть что угодно, кроме типа счетчика. Давайте небольшое описание для каждого тега.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
> ALTER TABLE website_tracker ADD tagsMap map<text, text>;
> UPDATE website_tracker SET tagsMap = {‘tag1’: ‘Tag One’} WHERE id = 1;
> SELECT tagsMap FROM website_tracker WHERE id = 1;
tagsMap
———————-
{‘tag1’: ‘Tag One’}
> UPDATE website_tracker SET tagsMap[‘tag2’] = ‘Tag Two’ WHERE id = 1;
> SELECT tagsMap FROM website_tracker WHERE id = 1;
tagsMap
——————
{‘tag1’: ‘Tag One’, ‘tag2’: ‘Tag Two’}
|
Определяемые пользователем типы данных
На Кассандре возможно определить наши собственные типы. Это дает большую гибкость и упрощает общее обслуживание данных. Допустим, мы хотим сохранить регистрационный адрес сайта.
|
1
2
3
4
5
6
|
> CREATE TYPE address (
… street text,
… city text,
… state text);
> ALTER TABLE website_tracker ADD reg_address address;
|
Чтобы использовать пользовательский тип во вложенной коллекции, нам нужно указать его как frozen коллекцию.
|
1
|
> ALTER TABLE website_tracker ADD reg_addresses map<text, frozen<address>>;
|
Использование GoCQL
Я предполагаю, что у вас есть некоторые знания по использованию Golang и настройке и установке пакетов.
Установка
Чтобы установить пакет gocql , выполните следующую команду из оболочки:
|
1
|
$ go get github.com/gocql/gocql
|
Теперь я создам скрипт Go, который объяснит концепции, необходимые для понимания gocql .
Написание сценария
main.go
|
01
02
03
04
05
06
07
08
09
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
|
package main
import (
«github.com/gocql/gocql»
«log»
«time»
)
func PerformOperations() {
// Provide the cassandra cluster instance here.
cluster := gocql.NewCluster(«127.0.0.1»)
// The authenticator is needed if password authentication is
// enabled for your Cassandra installation.
// be removed.
cluster.Authenticator = gocql.PasswordAuthenticator{
Username: «some_username»,
Password: «some_password»,
}
// gocql requires the keyspace to be provided before the session is created.
// In future there might be provisions to do this later.
cluster.Keyspace = «keyspace_name»
// This is time after which the creation of session call would timeout.
// This can be customised as needed.
cluster.Timeout = 5 * time.Second
cluster.ProtoVersion = 4
session, err := cluster.CreateSession()
if err != nil {
log.Fatalf(«Could not connect to cassandra cluster: %v», err)
}
// Check if the table already exists.
keySpaceMeta, _ := session.KeyspaceMetadata(«keyspace_name»)
if _, exists := keySpaceMeta.Tables[«person»];
// Create a table
session.Query(«CREATE TABLE person (» +
«id text, name text, phone text, » +
«PRIMARY KEY (id))»).Exec()
}
// DIY: Update table with something if it already exist.
// Insert record into table using prepared statements
session.Query(«INSERT INTO person (id, name, phone) VALUES (?, ?, ?)»,
«shalabh», «Shalabh Aggarwal», «1234567890»).Exec()
// DIY: Update existing record
// Select record and run some process on data fetched
var name string
var phone string
if err := session.Query(
«SELECT name, phone FROM person WHERE id=’shalabh'»).Scan(
&name, &phone);
if err != gocql.ErrNotFound {
log.Fatalf(«Query failed: %v», err)
}
}
log.Printf(«Name: %v», name)
log.Printf(«Phone: %v», phone)
// Fetch multiple rows and run process over them
iter := session.Query(«SELECT name, phone FROM person»).Iter()
for iter.Scan(&name, &phone) {
log.Printf(«Iter Name: %v», name)
log.Printf(«Iter Phone: %v», phone)
}
// DIY: Delete record
}
func main() {
PerformOperations()
}
|
Большинство рабочих концепций объясняются в самом коде выше. Некоторые моменты, которые стоит отметить, — это различные операции, используемые вместе с session.Query() . Помимо трех нижеприведенных операций, есть много других поддерживаемых, которые можно увидеть в документации .
-
Exec(): это просто выполнит запрос без возврата каких-либо строк. Возвращает ошибку, если есть. -
Scan(): при выполнении запроса выполняется копирование значений столбцов из первой строки, совпадающей в запросе, с переданными переменными. Это отбросило бы любые ряды кроме первого. -
Iter(): это выполнит запрос и вернет итератор, который затем будет работать так же, какScan()для каждой выбранной строки.
Запуск сценария
Чтобы запустить скрипт, выполните команду ниже в оболочке.
|
1
2
3
4
5
6
|
$ go run main.go
2017/02/03 12:53:40 Name: Shalabh Aggarwal
2017/02/03 12:53:40 Phone: 1234567890
2017/02/03 12:53:40 Iter Name: Shalabh Aggarwal
2017/02/03 12:53:40 Iter Phone: 1234567890
|
Вывод
Во второй части этой серии руководств мы рассмотрели различные встроенные типы данных, доступные в Cassandra. Мы также увидели, как работают типы коллекций и как пользовательские типы могут использоваться для гибкости общей схемы. Мы также увидели, как мы можем программно взаимодействовать с Кассандрой на Голанге, используя gocql. Этот пакет предлагает гораздо больше функциональных возможностей, которые можно изучить самостоятельно.