Статьи

Начало работы с Cassandra: типы данных CQL и использование GoCQL

В первой части этой серии руководств я рассмотрел основы 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.123 64-разрядное целое число со 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>>;

Я предполагаю, что у вас есть некоторые знания по использованию Golang и настройке и установке пакетов.

Чтобы установить пакет gocql , выполните следующую команду из оболочки:

1
$ go get github.com/gocql/gocql

Теперь я создам скрипт Go, который объяснит концепции, необходимые для понимания gocql .

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