Хой, джедаи и амазонки!
Сегодня разбираемся с базами данных. Очередная веха в обучении программированию - когда наши данные мы можем поместить и извлечь из хранилища, отличного от текстового файла (и тем более карты - map или слайса).
В первой части поста будет общая информация о базах данных. Затем два раздела касательно разработки базы данных и написания программы на Go для взаимодействия с данными и тестирование запросов.
В конце будет бонус, полезный для тех, кто планирует начать ходить на собеседования. Информацию получил в ТГ-чате от коллеги, с которым познакомился через этот блог (ссылка на чат в конце публикации).
1. История баз данных
История баз данных (БД) насчитывает долгое развитие, начиная с появления ранних форм организации данных, таких как "плоские файлы" (Flat file database) и иерархические базы данных.
"Плоские файлы" - это простейший метод хранения данных, в котором информация организуется часто в виде таблицы (файлы TSV), или строки с разделёнными запятыми значениями (файлы CSV), или обычные текстовые файлы.
Начнём с определений и значении БД в жизни общества.
1.1. Общее понимание
База данных - это организованная коллекция данных, которая обычно хранится и доступна для манипулирования с использованием программного обеспечения. Вариантов определения БД великое множество. Вот определение из Гражданского кодекса РФ, статья 1260:
Базой данных является представленная в объективной форме совокупность самостоятельных материалов (статей, расчётов, нормативных актов, судебных решений и иных подобных материалов), систематизированных таким образом, чтобы эти материалы могли быть найдены и обработаны с помощью электронной вычислительной машины (ЭВМ).
Данные в базе данных организованы таким образом, чтобы они могли быть легко доступны, управляемы и обновляемы.
В жизни людей базы данных используются повсеместно, например, для:
- хранения медицинских записей;
- банковских транзакций;
- личной информации;
- бронирования отелей, билетов для путешествия или кино;
- онлайн-покупок и т.д.
Базы данных играют важную роль в программировании и повседневной жизни, обеспечивая эффективное управление и доступ к информации, что делает их неотъемлемой частью современного мира.
Сами по себе базы данных - статичные объекты. Для того, чтобы работать с БД нужно специальное ПО - СУБД, (Database Management System, сокр. DBMS).
На заре создания БД, программисты создавали собственные методы для хранения, доступа и обработки данных. Это было более сложным процессом, чем использование современных СУБД.
В этом отличие БД от СУБД. Ещё такой пример, который мне нравится по отличию:
Представим, что база данных - это библиотека.
Для удобного поиска нужной книги, добавления или изъятия из фонда библиотеки; а может быть и расстановки стеллажей с книгами, нам нужен библиотекарь.
Система управления базами данных (СУБД) - как библиотекарь, который знает, где лежит каждая книга. И способен помочь нам быстро найти книгу, внести на нужную полку и нужный стеллаж новую книгу и так далее.
Короче, использование СУБД унифицирует доступ к данным, обеспечивая более надежное и эффективное управление информацией по сравнению с изобретением собственных методов доступа к БД. А ещё СУБД конечно позволяет эти БД создавать.
Вот 10 примеров популярных СУБД:
- Oracle Database. Компания-разработчик: Oracle Corporation
Страна-разработчик: США
Год издания: 1979
Тип базы данных: Реляционная - IBM Db2. Компания-разработчик: IBM
Страна-разработчик: США
Год издания: 1983
Тип базы данных: Реляционная - Microsoft SQL Server. Компания-разработчик: Microsoft Corporation
Страна-разработчик: США
Год издания: 1989
Тип базы данных: Реляционная - PostgreSQL. Компания-разработчик: PostgreSQL Global Development Group
Страна-разработчик: Разработчики PostgreSQL из разных стран
Год издания: 1989
Тип базы данных: Объектно-реляционная - MySQL. Компания-разработчик: Oracle
Страна-разработчик: США
Год издания: 1995
Тип базы данных: Реляционная - SQLite. Компания-разработчик: D. Richard Hipp
Страна-разработчик: США
Год издания: 2000
Тип базы данных: Встраиваемая реляционная - MongoDB. Компания-разработчик: MongoDB Inc.
Страна-разработчик: США
Год издания: 2009
Тип базы данных: NoSQL - Redis. Компания-разработчик: Redis Labs
Страна-разработчик: Израиль
Год издания: 2009
Тип базы данных: NoSQL - Cassandra. Компания-разработчик: Apache Software Foundation
Страна-разработчик: США
Год издания: 2008
Тип базы данных: NoSQL - Amazon RDS. Компания-разработчик: Amazon Web Services
Страна-разработчик: США
Год издания: 2009
Тип базы данных: Реляционная
1.2. Краткая хронология по БД
1.2.1. Истоки появления БД
С появлением компьютеров и автоматизации информационных процессов возникла потребность в эффективном хранении и управлении данными.
Перфокарты были одним из первых средств записи и хранения данных для ЭВМ. Они использовались в ранних электронных компьютерах, таких как Mark I, разработанного в 1944 году, для ввода программ и данных или машинах Конрада Цузе (см. публикацию #34. Астериск и амперсанд в Go. История появления знаков в коде).
В 1950х появились магнитные носители, такие как магнитные ленты и диски, которые стали более эффективными средствами хранения информации.
Расширение использования компьютеров в деловой среде и научных исследованиях привело в 1960-х к необходимости создания систем, способных работать с большими объемами данных.
1.2.2. Ограничения в развитии БД
Между появлением ЭВМ и первыми базами данных в 1960-е прошло значительное время по нескольким причинам:
- Технологические ограничения. В период после Второй мировой войны компьютеры были громоздкими, медленными и дорогостоящими. Не существовало подходящих средств хранения данных и методов эффективного их управления.
- Фокус на вычислениях. В первые десятилетия развития компьютеров основное внимание уделялось выполнению математических расчетов и научных исследований. В это время потребность в системах управления данными была менее очевидной.
- Недостаток стандартизации. В отсутствие стандартов и методологий для разработки баз данных и языков запросов, процесс создания таких систем был затруднен.
- Эволюция понимания потребностей. Постепенно стало понятно, что хранение и организация больших объемов данных имеют критическое значение для предприятий и научных исследований, что способствовало разработке эффективных методов управления информацией.
1.2.3. Первая база данных
Происхождение термина «база данных», учёный компьютерных дисциплин из Великабритании Т. Уильям (Билл) Олле (родился в 1993, умер в марте 2019) связывает с работами, проводимыми в начале 1960-х гг. по заказу военных организаций США (вспоминаем DARPA и прообраз Интернета - ARPANet в предыдущей публикации об истории Интернета).
Эти работы были представлены симпозиумах, организованных в 1964-1965 г. (по другим данным, с 1963 г.) компанией System Development Corporation (далее, SDC) в Санта-Монике, США.
В это время SDC разработала для DARPA первую иерархическую базу данных Time-Shared Data Management System (TSDMS) - разработка интенсивно использовалась в военных целях.
Термин "База данных" стал широко применяться в контексте хранения и получения данных и стал одним из ключевых понятий в развитии информационных технологий.
1.2.4. Развитие БД и СУБД
В 1966 году IBM, совместно с компаниями Rockwell и Caterpillar, спроектировала иерархическую СУБД IMS (Information Management System) в задачу которой входила обработка спецификаций изделий для сверхтяжёлого ракетоносителя «Сатурн-5» и шаттла «Аполлон».
С 1960-х по 1970-е годы, инженер компьютерных дисциплин (и пилот ВВС) из Великобритании Эдгар Кодд разработал модель реляционных баз данных (в возрасте 37-47 лет), которая стала стандартом в индустрии.
В 1974 году IBM выпустила System R, первую коммерческую реляционную СУБД. Эта модель обеспечила более гибкую и эффективную организацию данных. СУБД известна тем, что стала первой реализацией декларативного языка программирования (SQL), которая является стандартом запросов для реляционных БД.
В 1980-х и 1990-х годах были разработаны и выпущены тысячи коммерческих и открытых реляционных СУБД, таких как Oracle, Microsoft SQL Server, MySQL и PostgreSQL.
С развитием интернета и появлением больших объемов данных в 2000-х годах, нереляционные (NoSQL) базы данных стали популярными для обработки и хранения распределенных данных.
В настоящее время технологии баз данных активно развиваются, включая распределенные базы данных, графовые базы данных, колоночные базы данных, ин-мемори базы данных и другие инновационные подходы к хранению и обработке данных.
1.2.5. Будущее БД
Будущее баз данных (БД) предполагает несколько тенденций и направлений развития:
- Расширение возможностей обработки данных. Будущее баз данных включает в себя улучшенные методы обработки и анализа больших объемов данных, включая машинное обучение, искусственный интеллект и аналитику данных в реальном времени.
- Рост облачных технологий. Облачные базы данных будут продолжать набирать популярность, поскольку они обеспечивают масштабируемость, гибкость и удобство использования без необходимости инвестировать в собственную инфраструктуру.
- Усиление безопасности. В будущем базы данных будут сильно фокусировать на защите данных, включая шифрование, многофакторную аутентификацию и другие технологии безопасности.
- Развитие графенных баз данных. Графовые базы данных, которые ориентированы на эффективную работу с данными, имеющими сложные взаимосвязи, будут возрастающе востребованы для решения задач связанных с социальными сетями, сетями связи и т.д.
- Интеграция с Интернетом вещей (IoT). Базы данных будут развиваться для управления и анализа данных, собираемых из различных устройств IoT, что потребует разработки новых моделей данных и методов анализа.
- Разработка квантовых баз данных. В будущем могут появиться квантовые базы данных, которые будут использовать принципы квантовой механики для хранения и обработки информации, обеспечивая значительный рост скорости вычислений.
2. Проектирование базы данных в SQLite
С историей баз данных разобрались, поехали реализовывать на практике.
2.1. Разработка в терминале
Первую базу данных я создал в консольном приложении sqlite3. Вот как выглядит программа:
Давайте создадим простенькую базу данных. Пусть это будет база, хранящая задачи. По сути, это таблица с несколькими столбцами. Допустим, я хочу такие столбцы:
- Порядковый номер.
- Описание задачи.
- Статус задачи (поставлена, сделана, иное).
- Дата постановки задачи.
Вот как может выглядеть код для создания такой базы данных:
Я создал таблицу tasks. Она будет иметь расширение db - читается "ди-би", файл будет с именем tasks.db.
Можно через терминал посмотреть какие есть таблицы в подключённой БД командой
.tables
А также посмотреть какие есть колонки в таблице командой
.shema name_table:
Помимо работы в терминале, есть несколько программ с графическим интерфейсом для создания/импорта/экспорта баз данных.
2.2. Проектирование в приложениях с GUI
2.2.1. DB Browser for SQLite
Потестировал в приложении DB Browser for SQLite. В нём можно удобно посмотреть содержимое БД, отредактировать структуру и содержимое БД и создать новую БД. Выглядит программа примерно так:
2.2.2. dbdiagram.io
Также есть сайт для создания визуальных диаграмм баз данных. Удобно отслеживать связи между таблицами.
Сервис не поддерживает SQLite, но за счёт минимальной доработки кода, можно работать с сайтом.
2.2.3. DBeaver
DBeaver — кроссплатформенная программа для работы с базами данных с графическим интерфейсом.
С программой не работал, но сохраняю, т.к. попала в поле зрения.
3. Разработка через тестирование
На начальном этапе я не знал, что есть подходы к программированию. Знал один подход - "сразу кодить".
В действительности есть по крайней мере три основных похода:
- Каскадная модель.
- Итеративная разработка - планируй-реализуй-проверяй-дорабатывай и снова круг.
- Спиральная модель - объединение и дополнение двух предыдущих этапов.
На вид всё просто, в действительности каждый этап - это эволюция бизнес-процессов, чтобы сделать ПО более полезным для потребителя и более дешёвым. Способы разработки ПО - интересная тема, думаю, разберу её в отдельной публикации.
В моём случае при работе с базами данных, я попробовал метод разработки через тестирование (TDD, test-driven developmen). Эту методологию придумал программист из США Кент Бек в 1999 году, в возрасте 38 лет.
На мой взгляд TDD может являться частью как итеративной, так и спиральной модели.
Смысл TDD в следующем: разработчик пишет требования к коду в виде теста, а только затем пишет код приложения и проверяет его работоспособность; затем пишет новый тест (новое требование к коду) и расширяет код приложения.
Этапы разработки:
- Пишем модульный тест (на конкретную функцию).
- Проверяем, что тесты не проходят.
- Пишем код приложения.
- Проверяем, что тесты проходят.
- Рефакторинг.
- Повторяем цикл.
Визуально это выглядит так:
3.1. Концепция приложения
Перед написанием первого теста, нужно сформировать либо получить ТЗ (техническое задание). В предыдущем разделе я придумал ТЗ сам: список задач. У задач может быть три статуса:
- Поставлена;
- В работе;
- Выполнена.
Вперёд, пишем тесты.
3.2. Первое требование - первый тест
У нас готова общая концепция, что должно делать приложение. С чего начнём тестировать приложение?
3.2.1. Пишем первый тест
Начнём с создания самой базы данных. Соответственно, требование к приложению - чтобы программа могла создавать БД. Исходя из общей логики и понимания программирования, логично что будет некая функция, которая создаёт БД в приложении. Мы протестируем эту функцию, чтобы БД создавалась без ошибки:
В моих публикациях не было информации о тестировании. Информации об этом предостаточно в интернете или стандартной библиотеке.
Здесь отмечу, что при написании сложных тестов, удобно пользоваться не пакетом стандартной библиотеки testing, а внешним пакетом testify.
В тесте всё просто - я планирую, что в приложении будет функция, которая создаёт базу данных и возвращает ошибку. Соответственно, я проверяю является ли ошибка ниловой (ничем), и если нет - вывожу в терминал информацию. Короткий тест. Все последующие тесты в этой публикации будут в том же духе, но тесты могут быть самые разные, вот небольшая их часть:
- Проверка наличия ошибки (assert.Error).
- Проверка на срез значений: assert.ElementsMatch - позволяет проверить, что два среза имеют одинаковые элементы в любом порядке.
- Проверка значения с эталонным (assert.Equal).
- Проверка на неравенство значений (assert.NotEqual)
- Проверка на пустоту переменной (assert.Empty).
- Проверка на непустоту переменной (assert.NotEmpty).
- Проверка на истинность (assert.True).
- Проверка на ложь (assert.False).
- Проверка на равенство значений (assert.EqualValues).
- Проверка на содержание подстроки в строке (assert.Contains)
- Проверка на соответствие регулярному выражению (assert.Regexp)
В моём случае код теста будет выглядеть так:
Здесь я вызываю функцию 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. Пишем приложение
Добавленный код приложения выглядит так:
При запуске теста и успешном прохождении, вывод в терминал будет таким:
Ну и так далее. Появляется идея нового функционала приложения, действуем по алгоритму:
- Сперва продумываем что код должен уметь делать;
- Пишем для кода тест;
- Затем пишем код приложения.
Такой способ написания ПО способствует большому покрытию функционала приложения тестами, что обеспечит большую надёжность, стабильность работы и сокращает риск что приложение поведёт себя не так, как от него ожидали.
4. Выводы
Посмотрели наискосок предпосылки и историю возникновения баз данных. Разобрались, что такое БД и СУБД. Научились писать простенькие базы данных и писать код, покрытый тестами на примере работы с базой данных.
По поводу бонуса, о котором рассказывал в начале публикации. Есть общедоступная база данных по вопросам (и ответам) с собеседований. Как говорится, u a welcome.
Спасибо, что дочитал публикацию до конца. Если она тебе понравилась или ты узнал что-то новое и полезное, поддержи лайком и напиши комментарий. Успехов в изучении программирования. Будем на связи.
Бро, ты уже здесь? 👉 Подпишись на канал для новичков «Войти в IT» в Telegram, будем изучать IT вместе 👨💻👩💻👨💻