1. SQLite — самая часто разворачиваемая и используемая база данных. На текущий момент активно используется более одного триллиона (1000000000000 или миллиона миллионов) баз данных SQLite.

    Её поддерживают три человека. Они не допускают внешних контрибьюторов.
  2. Скорее всего, SQLite используется больше, чем все остальные движки баз данных суммарно. В мире работают миллиарды копий SQLite. Её можно встретить повсюду.


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


  4. За разработкой SQLite стоит компания Hwaci. Кажется, она занимается и музыкой (?).


  5. Изначально SQLite создавалась для корабля ВМС США. Ричард Хипп (D. Richard Hipp, DRH) разрабатывал ПО для эсминца USS Oscar Austin. Раньше ПО корабля просто переставало работать при выходе из строя сервера (это было в 2000-х). Для боевого корабля это неприемлемо.

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

    Его прямая речь:

    Зачем нам вообще нужен сервер? Почему бы не считывать данные напрямую с диска? В таком случае, если компьютер достаточно мощный, он сможет выполнять наше приложение, а у нас не будет зависимостей, способных сбоить и вызывать причины сбоев. Я поискал, но не нашёл ни одного движка баз данных SQL, умеющих такое. Один из коллег тогда спросил: «Ричард, а почему бы тебе просто самому не написать его?» «Ладно, попробую». Я не сделал этого сразу, но позже возникли проблемы с финансированием. Насколько я помню, в 2000-х у Ньюта Гингрича и Билла Клинтона возник какой-то конфликт, поэтому все государственные контракты были приостановлены. На несколько месяцев я потерял работу, поэтому подумал: «Что ж, тогда я просто напишу движок баз данных».

  6. В отличие от большинства опенсорсных проектов, использующих стандартные лицензии (например, MIT или GPL), SQLite не поставляется с одобренной OSI лицензией.


    SQLite сделали общественным достоянием (public domain), и она даже имеет ещё меньше ограничений.

    Стоит также отметить, что это может стать проблемой в некоторых странах/юрисдикциях, не признающих общественное достояние. Поэтому SQLite продаёт лицензии, называемые «Warranty of Title» («гарантия чистоты прав собственности»).
  7. Разработчики не допускают внешнего вклада в проект. Нельзя просто отправить пул-реквест и надеяться, что патч будет одобрен.


  8. Open Source, но не Open Contribution


    Контрибьютором в SQLite можно стать только по приглашению (у меня нет ссылки на источник, подтверждающий это). Патчи можно будет добавлять только после того, как вас пригласят и вы подпишете письменное поручительство (аффидавит) о том, что передаёте свой вклад в общественное достояние.
  9. Как там всё устроено?

    На каждую строку кода SQLite приходится более 600 строк кода тестов. Тесты покрывают 100% ветвлений (и 100% MC/DC) библиотеки. Набор тестов крайне разнообразен, он включает фаззинг-тесты, тесты граничных значений, регрессионные тесты и тесты, симулирующие вылеты операционных систем, отключение питания, ошибки ввода-вывода, ошибки out-of-memory.

    Изначально SQLite была расширением Tcl, а её основной набор тестов написан на Tcl.
  10. Любопытно, что некоторые тесты SQLite проприетарны. Набор тестов TH3 (Test Harness 3), обеспечивающий стопроцентное покрытие ветвлений кода, проприетарен, а доступ к нему закрыт.

    Не знаю ни одного другого проекта, который бы был открытым, но с платными тестами.

    Однако разработчики не смогли продать ни единой копии TH3. DRH сказал в подкасте:

    «Стопроцентные тесты MCD называются TH3. Они проприетарны. У меня была мысль, что мы можем продавать эти тесты производителям авиационного оборудования и зарабатывать на этом. Мы продали ровно ноль копий, так что затея провалилась».

    Для получения доступа нужно вступить в SQLite Consortium с ежегодным членским взносом 120 тысяч долларов.
  11. Это интересная бизнес-модель. Разработчики генерируют прибыль через лицензии, платную поддержку, сервисное обслуживание, членство в консорциуме и коммерческие расширения.
  12. У SQLite нет Code of Conduct (CoC), вместо него используется Code of Ethics, производный от «видов добрых дел» из главы 4 Устава святого Бенедикта.


  13. В SQLite вместо правовых положений благословение:


    Во всех файлах исходного кода есть благословение.
  14. SQLite настолько быстр, что конкурирует по скорости с fopen. В некоторых сценариях использования можно применять SQLite вместо файловой системы, это может быть на 35% быстрее.


  15. SQLite против Redis (угадайте, кто быстрее?)


    В некоторых сценариях использования SQLite может быть быстрее, чем Redis из-за сетевого стека и лишних затрат на (де)сериализацию.
  16. Но, в отличие от большинства баз данных, SQLite имеет модель единственного писателя. Одновременно может быть не больше одного писателя.

    В 2010 году ситуация изменилась из-за добавления режима WAL. До его появления могли быть или читатели, или писатель, но не одновременно.
  17. Есть и другие аспекты, очень часто встречающиеся в других базах данных, но не в SQLite:

    • По умолчанию используется режим rollback journal, который позволяет использовать или несколько читателей, или одного писателя.
    • Внешние ключи отключены; их нужно включать отдельно.
    • «Слабая типизация». В SQLite это называется «аффинностью типов». Этот принцип означает, что в столбец можно вставить что угодно, даже если вы определили тип. Строгую типизацию столбцов нужно включать отдельно (при помощи опции таблиц STRICT).
    • Многие команды ALTER, привычные по другим базам данных, не работают. Например, нельзя добавить ограничение к уже имеющемуся столбцу. (Недавно была добавлена возможность изменения имени столбца).


    Существует целый список странностей SQLite.
  18. Мне очень не нравится, что она не заставляет соблюдать типы. Всё полностью произвольно:

    CREATE TABLE user(id INTEGER);
    INSERT into user VALUES ("YOLO!"); --- Это сработает!

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

    CREATE TABLE t(value TIMMYSTAMP);

    Типа TIMMYSTAMP не существует, но SQLite спокойно принимает его.

    В SQLite есть пять типов: NULL, INTEGER, REAL, TEXT, BLOB. Хотите узнать нечто безумное? Аффинность типов работает при помощи сопоставления подстрок!

    CREATE TABLE t(value SPONGEBLOB) --- Это тип BLOB!

    Поэтому случается и такое:

    Имейте в виду, что объявленный тип «FLOATING POINT» придаст аффинность INTEGER, а не REAL, потому что в конце «POINT» есть подстрока «INT».

  19. Это одна из моих любимых историй. SQLite пришлось сменить стандартный префикс с sqlite_ на etilqs_, когда пользователи начали звонить разработчикам по ночам.


  20. SQLite очень серьёзно относится к обратной совместимости

    «Все релизы SQLite версии 3 могут читать и записывать файлы баз данных, созданные самым первым релизом SQLite 3 (версией 3.0.0), выпущенным 18 июня 2004 года. Вот это и называется «обратной совместимостью». Разработчики обещают сохранять обратную совместимость формата файлов баз данных для всех будущих релизов SQLite 3».

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


  22. Автор SQLite Ричард Хипп (D. Richard Hipp, DRH) не нашёл ни одной подходящей ему готовой системы контроля версий, поэтому написал собственную под названием Fossil. Разумеется, Fossil основана на SQLite.

    Это напомнило мне о том, как Линус написал Git.

    Также DRH написал собственный генератор парсеров Lemon.
  23. DRH написал B-дерево на основании алгоритма из книги Дональда Кнута «Искусство программирования»; при этом он писал код на борту самолёта.
  24. SQLite произносится как «Эс-Кью-Эл-айт» («Ess-Cue-El-ite»). Однако официального произношения не существует. На форумах по SQLite DRH оставил такое сообщение:

    Я написал SQLite и думаю, название должно произноситься, как «S-Q-L-ite». Но вы вполне можете произносить его, как угодно.

    :-)

▍ Источники


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

2. Я опубликовал эту статью на Twitter, и люди дали мне ссылки на дополнительные источники. Спасибо им: 1, 2, 3, 4, 5.

Telegram-канал со скидками, розыгрышами призов и новостями IT ?

Комментарии (66)


  1. Dynasaur
    15.01.2025 13:18

    Не понятно зачем для каждого эсминца писать своё ПО. Ещё и свою СУБД. Однопользовательскую.


    1. Volodichev
      15.01.2025 13:18

      USS Oscar Austin, это эсминец типа Арли Бёрк. Первый эсминец из серии IIa. Логично, что в новой серии кораблей обновили корабельные системы, по сравнению с предыдущими сериями.
      А когда на ПСИ новая система не заработала как надо, то решили разработать костыль. Костыль вышел удачным.


    1. a1excoder
      15.01.2025 13:18

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


  1. SergeiZababurin
    15.01.2025 13:18

    Спасибо. Мне про sqlite рассказывалточень сильный разработчик в Альпари когда работал.

    Прочитал статью, как в прошлое вернулся )


  1. Format-X22
    15.01.2025 13:18

    Для торговых ботов в связке с Rust хорошо подходит, как раз требование к надежности уровня военного корабля, много параллельных читателей-писателей не нужно, ну и меньше зависимостей - меньше точек отказа, никаких отдельных серверов или контейнеров, компактно и надежно. Все мои кейсы использования SQLite были такими.


  1. Nn111
    15.01.2025 13:18

    Очень удобно для десктопа. Вроде даже визуал студия данные проектов в склайт хранит.

    А так хоть и в настройках есть выбор многих фреймворков например, но в реальных веб сервисах ни разу не видел склайт.


    1. vvzvlad
      15.01.2025 13:18

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

      Думается мне, процентов 90 веб-сервисов используют sqlite :)


      1. randomsimplenumber
        15.01.2025 13:18

        Приблизительно 0%. У SQLite очень плохо с параллельной записью


        1. vvzvlad
          15.01.2025 13:18

          С какой параллельной, о чем вы?


          1. randomsimplenumber
            15.01.2025 13:18

            Ну, скажем, 2 клиента этого веб-сервиса одновременно нажали кнопку "обновить". Если клиентов больше 10 - сервис будет очень сильно тупить.


            1. vvzvlad
              15.01.2025 13:18

              Я про LocalStorage


              1. randomsimplenumber
                15.01.2025 13:18

                Это больше к браузеру.


                1. vvzvlad
                  15.01.2025 13:18

                  Веб-приложения же в нем работают


              1. mayorovp
                15.01.2025 13:18

                Веб-сервис не может использовать LocalStorage, потому что работает не в браузере.


                1. vvzvlad
                  15.01.2025 13:18

                  Половина веб-сервиса работает в браузере.


                  1. mayorovp
                    15.01.2025 13:18

                    Нет, не работает.

                    У веб-сервиса может быть браузерный клиент или приложенное к нему веб-приложение (скажем, для администрирования), но ни то, ни другое не являются составными частями веб-сервиса.


                    1. vvzvlad
                      15.01.2025 13:18

                      Ага. У Gmail браузерная часть кода не является частью веб-приложения, ясно, понятно.


                      1. mayorovp
                        15.01.2025 13:18

                        А с каких пор gmail - веб-сервис? Вы же сами написали: веб-приложение. Это разные термины. Совсем разные.


        1. uranik
          15.01.2025 13:18

          Так вэб сервис все параллельные записи может выстроить в последовательную очередь и быстро записать в sqlite.


          1. randomsimplenumber
            15.01.2025 13:18

            Ну, можно конечно изобрести свой движок поверх SQLite. недоmysql ;)


          1. mayorovp
            15.01.2025 13:18

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


        1. ShashkovS
          15.01.2025 13:18

          У вас устаревшие данные.

          import os
          import sqlite3
          import time
          import multiprocessing
          import uuid
          
          
          def writer_process(proc_id, num_inserts, db_file):
              conn = sqlite3.connect(db_file)
              conn.execute("PRAGMA busy_timeout = 5000")
              conn.execute("PRAGMA synchronous = NORMAL")
              for _ in range(num_inserts):
                  with conn:
                      conn.execute("INSERT INTO test_data (val) VALUES (?)", (str(uuid.uuid4()),))
          
          
          def run(num_processes, num_inserts, test_id=1):
              start_time = time.perf_counter()
              db_name = f'db{test_id}.sqlite'
              conn = sqlite3.connect(db_name)
              conn.execute("PRAGMA journal_mode = WAL")
              conn.execute("CREATE TABLE IF NOT EXISTS test_data (id INTEGER PRIMARY KEY, val TEXT)")
              conn.commit()
              conn.close()
          
              processes = []
              for i in range(num_processes):
                  p = multiprocessing.Process(target=writer_process, args=(i, num_inserts, db_name))
                  processes.append(p)
          
              print(f'Тестируем {num_processes} процессов')
              for p in processes:
                  p.start()
          
              for p in processes:
                  p.join()
          
              os.unlink(db_name)
          
              total_time = time.perf_counter() - start_time
              total_inserts = num_processes * num_inserts
              print(
                  f"{num_processes} процессов. Общее время: {total_time:.4f} секунд. Суммарная скорость: {total_inserts / total_time:.2f} транзакций/сек")
          
          
          if __name__ == "__main__":
              num_inserts = 277200
              for num_processes in range(1, 21):
                  run(num_processes, num_inserts // num_processes)
          

          У меня максимум на 4 одновременных процессах --- 70К транзакций в секунду.
          Дальше до 20 процессов --- около 30К транзакций в секунду. И это в режиме, когда все процессы только и делают, что пишут в одную общую нагруженную таблицу.

          Если вы пишете асинхронный вебсервис, то там будет столько воркеров, сколько у вас физических ядер. То есть до 20 воркеров --- всё ок, это достаточно высокая нагрузка.
          Не high load, конечно, но..


  1. mysherocker
    15.01.2025 13:18

    Автор SQLite Ричард Хипп (D. Richard Hipp, DRH) не нашёл ни одной подходящей ему готовой системы контроля версий, поэтому написал собственную под названием Fossil. Разумеется, Fossil основана на SQLite.

    Это напомнило мне о том, как Линус написал Git.

    Это неудивительно, ведь Fossil вышел в 2006 году, не так уж сильно позже Git, разработка которого только началась в апреле 2025. Вероятно, разработка Fossil началась до публикации Git. А даже если и после, то тогда Git точно не был распространённой системой контроля версий.

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


    1. aMster1
      15.01.2025 13:18

      Человек из будущего, скажи, стоит брать битки?

      Или ты просто год разработки git попутал?


      1. Metotron0
        15.01.2025 13:18

        А у вас сейчас эра до отделения маскианцев от Земли или после? Эти старые эпохи так трудно запоминать.