Найти тему
Я, Golang-инженер

#57. SQLite в Golang: история БД, отличие БД от СУБД, знакомство с приложениями для с SQLite. Разработка БД в Go через тестирование

Оглавление

Хой, джедаи и амазонки!

Сегодня разбираемся с базами данных. Очередная веха в обучении программированию - когда наши данные мы можем поместить и извлечь из хранилища, отличного от текстового файла (и тем более карты - map или слайса).

В первой части поста будет общая информация о базах данных. Затем два раздела касательно разработки базы данных и написания программы на Go для взаимодействия с данными и тестирование запросов.

В конце будет бонус, полезный для тех, кто планирует начать ходить на собеседования. Информацию получил в ТГ-чате от коллеги, с которым познакомился через этот блог (ссылка на чат в конце публикации).

1. История баз данных

История баз данных (БД) насчитывает долгое развитие, начиная с появления ранних форм организации данных, таких как "плоские файлы" (Flat file database) и иерархические базы данных.

"Плоские файлы" - это простейший метод хранения данных, в котором информация организуется часто в виде таблицы (файлы TSV), или строки с разделёнными запятыми значениями (файлы CSV), или обычные текстовые файлы.
Источник иллюстрации: https://vk.cc/cubXtn
Источник иллюстрации: https://vk.cc/cubXtn

Начнём с определений и значении БД в жизни общества.

1.1. Общее понимание

База данных - это организованная коллекция данных, которая обычно хранится и доступна для манипулирования с использованием программного обеспечения. Вариантов определения БД великое множество. Вот определение из Гражданского кодекса РФ, статья 1260:

Базой данных является представленная в объективной форме совокупность самостоятельных материалов (статей, расчётов, нормативных актов, судебных решений и иных подобных материалов), систематизированных таким образом, чтобы эти материалы могли быть найдены и обработаны с помощью электронной вычислительной машины (ЭВМ).

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

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

  • хранения медицинских записей;
  • банковских транзакций;
  • личной информации;
  • бронирования отелей, билетов для путешествия или кино;
  • онлайн-покупок и т.д.
Иллюстрация: https://vk.cc/cubY9i
Иллюстрация: https://vk.cc/cubY9i

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

Сами по себе базы данных - статичные объекты. Для того, чтобы работать с БД нужно специальное ПО - СУБД, (Database Management System, сокр. DBMS).

На заре создания БД, программисты создавали собственные методы для хранения, доступа и обработки данных. Это было более сложным процессом, чем использование современных СУБД.

В этом отличие БД от СУБД. Ещё такой пример, который мне нравится по отличию:

Представим, что база данных - это библиотека.
Для удобного поиска нужной книги, добавления или изъятия из фонда библиотеки; а может быть и расстановки стеллажей с книгами, нам нужен библиотекарь.
Система управления базами данных (СУБД) - как библиотекарь, который знает, где лежит каждая книга. И способен помочь нам быстро найти книгу, внести на нужную полку и нужный стеллаж новую книгу и так далее.
Короче, использование СУБД унифицирует доступ к данным, обеспечивая более надежное и эффективное управление информацией по сравнению с изобретением собственных методов доступа к БД. А ещё СУБД конечно позволяет эти БД создавать.
Источник: https://vk.cc/cue8Hi
Источник: https://vk.cc/cue8Hi

Вот 10 примеров популярных СУБД:

  1. Oracle Database. Компания-разработчик: Oracle Corporation
    Страна-разработчик: США
    Год издания: 1979
    Тип базы данных: Реляционная
  2. IBM Db2. Компания-разработчик: IBM
    Страна-разработчик: США
    Год издания: 1983
    Тип базы данных: Реляционная
  3. Microsoft SQL Server. Компания-разработчик: Microsoft Corporation
    Страна-разработчик: США
    Год издания: 1989
    Тип базы данных: Реляционная
  4. PostgreSQL. Компания-разработчик: PostgreSQL Global Development Group
    Страна-разработчик: Разработчики PostgreSQL из разных стран
    Год издания: 1989
    Тип базы данных: Объектно-реляционная
  5. MySQL. Компания-разработчик: Oracle
    Страна-разработчик: США
    Год издания: 1995
    Тип базы данных: Реляционная
  6. SQLite. Компания-разработчик: D. Richard Hipp
    Страна-разработчик: США
    Год издания: 2000
    Тип базы данных: Встраиваемая реляционная
  7. MongoDB. Компания-разработчик: MongoDB Inc.
    Страна-разработчик: США
    Год издания: 2009
    Тип базы данных: NoSQL
  8. Redis. Компания-разработчик: Redis Labs
    Страна-разработчик: Израиль
    Год издания: 2009
    Тип базы данных: NoSQL
  9. Cassandra. Компания-разработчик: Apache Software Foundation
    Страна-разработчик: США
    Год издания: 2008
    Тип базы данных: NoSQL
  10. Amazon RDS. Компания-разработчик: Amazon Web Services

Страна-разработчик: США

Год издания: 2009

Тип базы данных: Реляционная

1.2. Краткая хронология по БД

1.2.1. Истоки появления БД

С появлением компьютеров и автоматизации информационных процессов возникла потребность в эффективном хранении и управлении данными.

Перфокарты были одним из первых средств записи и хранения данных для ЭВМ. Они использовались в ранних электронных компьютерах, таких как Mark I, разработанного в 1944 году, для ввода программ и данных или машинах Конрада Цузе (см. публикацию #34. Астериск и амперсанд в Go. История появления знаков в коде).

Узлы ввода-вывода компьютера Mark I фирмы IBM
Узлы ввода-вывода компьютера Mark I фирмы IBM

В 1950х появились магнитные носители, такие как магнитные ленты и диски, которые стали более эффективными средствами хранения информации.

Расширение использования компьютеров в деловой среде и научных исследованиях привело в 1960-х к необходимости создания систем, способных работать с большими объемами данных.

1.2.2. Ограничения в развитии БД

Между появлением ЭВМ и первыми базами данных в 1960-е прошло значительное время по нескольким причинам:

  1. Технологические ограничения. В период после Второй мировой войны компьютеры были громоздкими, медленными и дорогостоящими. Не существовало подходящих средств хранения данных и методов эффективного их управления.
  2. Фокус на вычислениях. В первые десятилетия развития компьютеров основное внимание уделялось выполнению математических расчетов и научных исследований. В это время потребность в системах управления данными была менее очевидной.
  3. Недостаток стандартизации. В отсутствие стандартов и методологий для разработки баз данных и языков запросов, процесс создания таких систем был затруднен.
  4. Эволюция понимания потребностей. Постепенно стало понятно, что хранение и организация больших объемов данных имеют критическое значение для предприятий и научных исследований, что способствовало разработке эффективных методов управления информацией.
Источник: https://vk.cc/cuebQw
Источник: https://vk.cc/cuebQw

1.2.3. Первая база данных

Происхождение термина «база данных», учёный компьютерных дисциплин из Великабритании Т. Уильям (Билл) Олле (родился в 1993, умер в марте 2019) связывает с работами, проводимыми в начале 1960-х гг. по заказу военных организаций США (вспоминаем DARPA и прообраз Интернета - ARPANet в предыдущей публикации об истории Интернета).

Эти работы были представлены симпозиумах, организованных в 1964-1965 г. (по другим данным, с 1963 г.) компанией System Development Corporation (далее, SDC) в Санта-Монике, США.

В это время SDC разработала для DARPA первую иерархическую базу данных Time-Shared Data Management System (TSDMS) - разработка интенсивно использовалась в военных целях.

Полуавтоматическая система управления средствами системы ПВО США — один из первых крупных исследовательских проектов корпорации SDC - фото Википедии
Полуавтоматическая система управления средствами системы ПВО США — один из первых крупных исследовательских проектов корпорации SDC - фото Википедии

Термин "База данных" стал широко применяться в контексте хранения и получения данных и стал одним из ключевых понятий в развитии информационных технологий.

1.2.4. Развитие БД и СУБД

В 1966 году IBM, совместно с компаниями Rockwell и Caterpillar, спроектировала иерархическую СУБД IMS (Information Management System) в задачу которой входила обработка спецификаций изделий для сверхтяжёлого ракетоносителя «Сатурн-5» и шаттла «Аполлон».

С 1960-х по 1970-е годы, инженер компьютерных дисциплин (и пилот ВВС) из Великобритании Эдгар Кодд разработал модель реляционных баз данных (в возрасте 37-47 лет), которая стала стандартом в индустрии.

Эдгар Кодд. Источник: https://mavink.com/explore/Famous-Computer-Scientists
Эдгар Кодд. Источник: https://mavink.com/explore/Famous-Computer-Scientists

В 1974 году IBM выпустила System R, первую коммерческую реляционную СУБД. Эта модель обеспечила более гибкую и эффективную организацию данных. СУБД известна тем, что стала первой реализацией декларативного языка программирования (SQL), которая является стандартом запросов для реляционных БД.

В 1980-х и 1990-х годах были разработаны и выпущены тысячи коммерческих и открытых реляционных СУБД, таких как Oracle, Microsoft SQL Server, MySQL и PostgreSQL.

С развитием интернета и появлением больших объемов данных в 2000-х годах, нереляционные (NoSQL) базы данных стали популярными для обработки и хранения распределенных данных.

В настоящее время технологии баз данных активно развиваются, включая распределенные базы данных, графовые базы данных, колоночные базы данных, ин-мемори базы данных и другие инновационные подходы к хранению и обработке данных.

1.2.5. Будущее БД

Источник: https://vk.cc/cuecZZ
Источник: https://vk.cc/cuecZZ

Будущее баз данных (БД) предполагает несколько тенденций и направлений развития:

  1. Расширение возможностей обработки данных. Будущее баз данных включает в себя улучшенные методы обработки и анализа больших объемов данных, включая машинное обучение, искусственный интеллект и аналитику данных в реальном времени.
  2. Рост облачных технологий. Облачные базы данных будут продолжать набирать популярность, поскольку они обеспечивают масштабируемость, гибкость и удобство использования без необходимости инвестировать в собственную инфраструктуру.
  3. Усиление безопасности. В будущем базы данных будут сильно фокусировать на защите данных, включая шифрование, многофакторную аутентификацию и другие технологии безопасности.
  4. Развитие графенных баз данных. Графовые базы данных, которые ориентированы на эффективную работу с данными, имеющими сложные взаимосвязи, будут возрастающе востребованы для решения задач связанных с социальными сетями, сетями связи и т.д.
  5. Интеграция с Интернетом вещей (IoT). Базы данных будут развиваться для управления и анализа данных, собираемых из различных устройств IoT, что потребует разработки новых моделей данных и методов анализа.
  6. Разработка квантовых баз данных. В будущем могут появиться квантовые базы данных, которые будут использовать принципы квантовой механики для хранения и обработки информации, обеспечивая значительный рост скорости вычислений.

2. Проектирование базы данных в SQLite

С историей баз данных разобрались, поехали реализовывать на практике.

2.1. Разработка в терминале

Первую базу данных я создал в консольном приложении sqlite3. Вот как выглядит программа:

Работа в терминале с sqlite3
Работа в терминале с sqlite3

Давайте создадим простенькую базу данных. Пусть это будет база, хранящая задачи. По сути, это таблица с несколькими столбцами. Допустим, я хочу такие столбцы:

  1. Порядковый номер.
  2. Описание задачи.
  3. Статус задачи (поставлена, сделана, иное).
  4. Дата постановки задачи.

Вот как может выглядеть код для создания такой базы данных:

Работа в терминале
Работа в терминале

Я создал таблицу tasks. Она будет иметь расширение db - читается "ди-би", файл будет с именем tasks.db.

Можно через терминал посмотреть какие есть таблицы в подключённой БД командой

.tables

А также посмотреть какие есть колонки в таблице командой

.shema name_table:
Работа в терминале
Работа в терминале

Помимо работы в терминале, есть несколько программ с графическим интерфейсом для создания/импорта/экспорта баз данных.

2.2. Проектирование в приложениях с GUI

2.2.1. DB Browser for SQLite

Потестировал в приложении DB Browser for SQLite. В нём можно удобно посмотреть содержимое БД, отредактировать структуру и содержимое БД и создать новую БД. Выглядит программа примерно так:

Интерфейс DB Browser for SQLite
Интерфейс DB Browser for SQLite

2.2.2. dbdiagram.io

Также есть сайт для создания визуальных диаграмм баз данных. Удобно отслеживать связи между таблицами.

Сервис не поддерживает SQLite, но за счёт минимальной доработки кода, можно работать с сайтом.

Тестовая БД со связями между таблицами
Тестовая БД со связями между таблицами

2.2.3. DBeaver

DBeaver — кроссплатформенная программа для работы с базами данных с графическим интерфейсом.

С программой не работал, но сохраняю, т.к. попала в поле зрения.

3. Разработка через тестирование

На начальном этапе я не знал, что есть подходы к программированию. Знал один подход - "сразу кодить".

В действительности есть по крайней мере три основных похода:

  1. Каскадная модель.
  2. Итеративная разработка - планируй-реализуй-проверяй-дорабатывай и снова круг.
  3. Спиральная модель - объединение и дополнение двух предыдущих этапов.

На вид всё просто, в действительности каждый этап - это эволюция бизнес-процессов, чтобы сделать ПО более полезным для потребителя и более дешёвым. Способы разработки ПО - интересная тема, думаю, разберу её в отдельной публикации.

В моём случае при работе с базами данных, я попробовал метод разработки через тестирование (TDD, test-driven developmen). Эту методологию придумал программист из США Кент Бек в 1999 году, в возрасте 38 лет.

Кент Бек - фото с Википедии
Кент Бек - фото с Википедии

На мой взгляд TDD может являться частью как итеративной, так и спиральной модели.

Смысл TDD в следующем: разработчик пишет требования к коду в виде теста, а только затем пишет код приложения и проверяет его работоспособность; затем пишет новый тест (новое требование к коду) и расширяет код приложения.

Этапы разработки:

  1. Пишем модульный тест (на конкретную функцию).
  2. Проверяем, что тесты не проходят.
  3. Пишем код приложения.
  4. Проверяем, что тесты проходят.
  5. Рефакторинг.
  6. Повторяем цикл.

Визуально это выглядит так:

Цикл разработки по TDD в виде блок-схемы - иллюстрация с Википедии
Цикл разработки по TDD в виде блок-схемы - иллюстрация с Википедии

3.1. Концепция приложения

Перед написанием первого теста, нужно сформировать либо получить ТЗ (техническое задание). В предыдущем разделе я придумал ТЗ сам: список задач. У задач может быть три статуса:

  • Поставлена;
  • В работе;
  • Выполнена.
Иллюстрация - https://vk.cc/cucqGw
Иллюстрация - https://vk.cc/cucqGw

Вперёд, пишем тесты.

3.2. Первое требование - первый тест

У нас готова общая концепция, что должно делать приложение. С чего начнём тестировать приложение?

3.2.1. Пишем первый тест

Начнём с создания самой базы данных. Соответственно, требование к приложению - чтобы программа могла создавать БД. Исходя из общей логики и понимания программирования, логично что будет некая функция, которая создаёт БД в приложении. Мы протестируем эту функцию, чтобы БД создавалась без ошибки:

Код теста с пакетом testing
Код теста с пакетом testing

В моих публикациях не было информации о тестировании. Информации об этом предостаточно в интернете или стандартной библиотеке.

Здесь отмечу, что при написании сложных тестов, удобно пользоваться не пакетом стандартной библиотеки testing, а внешним пакетом testify.

В тесте всё просто - я планирую, что в приложении будет функция, которая создаёт базу данных и возвращает ошибку. Соответственно, я проверяю является ли ошибка ниловой (ничем), и если нет - вывожу в терминал информацию. Короткий тест. Все последующие тесты в этой публикации будут в том же духе, но тесты могут быть самые разные, вот небольшая их часть:

  1. Проверка наличия ошибки (assert.Error).
  2. Проверка на срез значений: assert.ElementsMatch - позволяет проверить, что два среза имеют одинаковые элементы в любом порядке.
  3. Проверка значения с эталонным (assert.Equal).
  4. Проверка на неравенство значений (assert.NotEqual)
  5. Проверка на пустоту переменной (assert.Empty).
  6. Проверка на непустоту переменной (assert.NotEmpty).
  7. Проверка на истинность (assert.True).
  8. Проверка на ложь (assert.False).
  9. Проверка на равенство значений (assert.EqualValues).
  10. Проверка на содержание подстроки в строке (assert.Contains)
  11. Проверка на соответствие регулярному выражению (assert.Regexp)

В моём случае код теста будет выглядеть так:

Код теста с внешним пакетом testify
Код теста с внешним пакетом testify

Здесь я вызываю функцию assert пакета testify с функцией NoError. NoError проверяет, является ли передаваемая ошибка ниловой, или нет. Без построения условий типа if err != nil...

Как я понимаю, весь функционал пакета testify доступен в пакете testing. Но чтобы добиться того же результата, потребуется многословие кода.

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

Далее буду использовать пакет testify в связке со стандартным пакетом testing.

3.2.2. Пишем код приложения

Пишем код приложения. В функции main вызываем функцию, которую будем тестировать:

Код приложения
Код приложения

Что здесь интересного: если удалить строки 23-32, то база данных в моём понимании будет создана за счёт строк 18-22 - но из-за отсутствия в ней таблиц, тут же удаляется.

Если вы ранее не писали тесты, то при корректной отработке теста в терминале будет примерно следующее:

Терминал
Терминал

Ещё интересная вещь: если запустить код через тестовую функцию go test или go run main.go, то создаётся база данных. Как проверить, не затрёт ли существующую БД повторный запуск кода если не принимать во внимание код "...IF NOT EXISTS..."?

В Linux Mandjaro в которой я работаю, через графический интерфейс не посмотреть время создания файла. Тогда я воспользовался командой

stat tasks.db, - где tasks.db - имя файла БД, чтобы проверить время создания файла.

В результате я убедился, что файл не затирайтся, если БД уже существует:

Проверка времени создания БД через терминал
Проверка времени создания БД через терминал

Что здесь важно

Мы тестируем функции пакета main, но не тестируем код, который вызывает эти функции из функции main.

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

Есть так называемые интеграционные тесты - там всё посложнее, и обычно их проводят после модульных тестов (на отдельные функции). Касаться их пока не буду.

3.3. Второе требование - второй тест

Первый фрагмент кода приложения написан и успешно протестирован. Пора расширять требования к коду приложения.

3.3.1. Пишем второй тест

База данных создана. Самое время протестировать добавление в неё нового функционала: пишем в БД задачу. Также мне важно, чтобы в БД появлялась текущая дата-время поставленной задачи и присваивался статус задачи "Поставлена".

Файл с тестами дополняется двумя функциями:

Код теста
Код теста

Здесь я подразумеваю, что в исходном коде (приложении) должна появиться соответствующая структура Task. Поле ID я не объявляю, т.к. оно в БД задано с автоинкрементном, т.е. само присваивается и каждый раз увеличивает на единицу значение новой записи.

Важный момент:

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

3.3.2. Пишем код приложения

Сперва добавим новые необходимые константы и структуру:

Фрагмент кода
Фрагмент кода

Далее пишу функцию:

Фрагмент кода
Фрагмент кода

Результат запуска теста будет выглядеть так:

Результат работы теста
Результат работы теста

3.4. Третья итерация теста

Сейчас будем изменять статус задачи - это новое требование к функционалу приложения.

3.4.1. Пишем тест

Так выгляди код теста:

Фрагмент кода
Фрагмент кода

3.4.2. Пишем приложение

Добавленный код приложения выглядит так:

Фрагмент кода приложения
Фрагмент кода приложения

При запуске теста и успешном прохождении, вывод в терминал будет таким:

Вывод в терминал
Вывод в терминал

Ну и так далее. Появляется идея нового функционала приложения, действуем по алгоритму:

  1. Сперва продумываем что код должен уметь делать;
  2. Пишем для кода тест;
  3. Затем пишем код приложения.

Такой способ написания ПО способствует большому покрытию функционала приложения тестами, что обеспечит большую надёжность, стабильность работы и сокращает риск что приложение поведёт себя не так, как от него ожидали.

4. Выводы

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

По поводу бонуса, о котором рассказывал в начале публикации. Есть общедоступная база данных по вопросам (и ответам) с собеседований. Как говорится, u a welcome.

Спасибо, что дочитал публикацию до конца. Если она тебе понравилась или ты узнал что-то новое и полезное, поддержи лайком и напиши комментарий. Успехов в изучении программирования. Будем на связи.

Источник: https://vk.cc/cuedAR
Источник: https://vk.cc/cuedAR

Бро, ты уже здесь? 👉 Подпишись на канал для новичков «Войти в IT» в Telegram, будем изучать IT вместе 👨‍💻👩‍💻👨‍💻