СУБД-строительные верфи

Всем Привет,

Скоро начну имплементировать движок аналитических запросов для СУБД похожей на Монго. Возник непростой вопрос. Что использовать ? Подход Монго с его Aggregate Framework или SQL подход.

UPD:

Большинство комментариев отдало предпочтение SQL.

Плюсы

1. Его знают достаточно много людей. Это дефакто стандарт.

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

Минусы

1. Плохо (или неочевидно) ложится на Json документы.

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

{
   "Name": "John",
   "Profession": "Lawyer",
   "Address": {
       "Street": "MyStreet",
       "HouseNumber": 10
   },
   "Childrens": [
       {
          "Name": "Bob",
          "Age": 5
       }
   ]
}

Простейший пример, отискать все документы где «Profession» = «Lawyer».

SELECT DocID, Name, Profession 
FROM Person 
WHERE Profession = "Lawyer" 
ORDER BY Name

DocID здесь — это служебная колонка, которая автоматически добавляется и содержит внутреннее айди документа в субд.

Как видим, проблема с простейшим запросом в том, что мы можем выбрать только атрибуты верхнего уровня в json. Чтобы научить наш SELECT работать с вложенными секциями документа, нам нужно указать путь к вложенной секции. Этот оператор получает первым параметром имя таблицы, а вторым параметром путь к секции.

Давайте получим все адресса со всех документов.

SELECT DocID, Street, HouseNumber
FROM Person.Address

И получим всех Childrens со всех документов.

SELECT DocID, Name, Age
FROM Person.Childrens[]

Теперь у нас есть 3 таблицы. Первая таблица у нас возвращает всех Person. Вторая все Address, а третья всех Childrens. Теперь все что нам нужно, заджойнить эти три таблицы и пазл сошелся.

SELECT * 
FROM Person p
INNER JOIN Person.Address a ON p.DocID = a.DocID
INNER JOIN Person.Childrens[] c ON p.DocID = c.DocID
WHERE p.Name = "John" AND c.Age = 5

По-моему получился достаточно локаничный синтаксис. Хватило всего одного приема чтобы подружить SQL и Json.

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

👍ПодобаєтьсяСподобалось1
До обраногоВ обраному3
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Да, интересная задача попалась.
Предоположим у нас есть 100 тыс клиентов базы данных, которые запускают тяжелые запросы по центральной базе данных, на основе какого-то SELECT ... FROM ... WHERE. Причем этот SELECT ... FROM ... WHERE у каждого клиента свой, индивидуальный.
Можно ли центральную базу данных написать так, чтобы клиенты вместо того, чтобы каждый раз дергать SELECT ... FROM ... WHERE на базе, просто подписывались (subscription) на изменения данных и сервер просто досылал новые данные которые подпадают под запрос клиента. Причем сервер не выполняет (!) запрос заново, он идет по более короткому пути, анализируя изменения на таблицах и может обрабатывать сотни тысяч подписчиков одновременно.
Соответственно клиенты сервера не только не выполняют SELECT ... FROM ... WHERE на каждый рефреш они еще и получают данные асинхронно, как только они изменились на сервере.

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

Сейчас я понимаю что такой механизм построить реально. Это полностью инвертирует понимание, _как_ может строится взаимодействие между клиентом и сервером.

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

А не простіше зробити реплікацію основної бази на 100 реплік і нехай ті 100 000 клієнтів використовують тільки репліки. Оскільки ті репліки будуть використовуватись тільки на читання, то експерт з конфігурації баз ще й зможе їх оптимально для такої задачі наконфігурити.

Реплика это дорого. А еще это не секьюрно. Например в христоматийной задаче с мессенджером, у вас все сообщения могут быть собраны в одном месте, но на ваш мессенджер отправляются только те сообщения, к которым вы имеете отношение. Тоже с почтовой рассылкой. Тоже с социальной сетью. Тоже с любой црм в которой есть секурити и вы работаете со своим срезом данных. Примеров много. Поэтому реплика годится только в задаче когда вам нужно именно полная реплика.

Використання репліки для клієнтів бази які тільки читають дані — це класичний підхід для зниження навантаження на основну базу. У вашому випадку підтримувати 100 реплік може бути занадто дорого, а може і не бути. Принаймні це просте рішення в лоб імплементація якого майже нічого не коштує. Про секюріті не зрозумів, ви шо хочете репліку бази в клієнта хостити. Ви можете всі 100 реплік в одному клауді тримати, щоб реплікація швидше відбувалась.

Цей підхід повністю вирішує вашу проблему, от тільки чи це ефективно це вже вам треба аналізувати.

У вашому випадку підтримувати 100 реплік

Речь шла о 100 тысяч реплик. Например в каждом мессендреже есть своя микробаза сообщений. В фейсбуке своя микробаза в которой хранятся данные о друзьях и их лентах новостей.

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

Так вам не треба на кожного клієнта мати свою репліку, вам достатньо зняти навантаження з основної бази, клієнти і далі будуть витягувати свої дані із бази просто це навантаження на базу розподілиться на 100 реплік.

Тобто ви взагалі клієнтів не змінюєте, вони і далі шлють свої SELECT запити до бази, тільки не до основної, а до однієї із 100 реплік.

Реплика это плохое решение. Уже получше но все ещё не так хорошо — это шардирование. А самое лучшее решение это шард и есть сам клиент

Ну что ребята, как говорил старина Сальвадор Дали,
"не бойтесь совершенства, вам его никогда не достичь"©
40 лет нас водили по говнокоду, навязывали писать круды, не могли нормально подружить базу данных с юаем. Но близится час освобождения (!). Теперь чтобы добавить в приложение новый UI диалог, нужно всеголишь представить, как написать SELECT запрос, который выберет нужные данные из одного или нескольких источников. Написав такой запрос, можно автоматически на основе его построить UI диалог, который будет редактировать выбранные данные. CRUD теперь схлопнулся до R.

Успели написать по базе 10 селектов к обеду, получили из коробки 10 новых UI диалогов.
Правда нативных (автогенерируемых), художественная верстка и дизайн сюда конечно не входит. Но вопрос с ОРМ и прослойкой репозиторных методов закрыт полностью.

Сегодня совершенно случайно нашел ещё один дименшин. В базе часто в каждой таблице длбавляют поля CreatedBy, DeletedBy, UpdatedBy и такие же CreatedOn UpdatedOn DeletedOn. А потом заполнение этих полей паровозом идет сквозь все процедуры, сквозь всю бизнесс логику, засоряя суть самой бизнесс логики.
На основе аспектной логики создаётся ещё одно измерение которое хранит эти 6 полей. В итоге контент документов отделен от служебных полей.

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

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

PS: назову дименшин WhoModifiedDimension

Проблемы проектирования от того, что есть разные архитектурные силы:
* Хочется дешево
* Хочется быстро накодить
* Хочется чтобы быстро работало
* Хочется чтобы работало на старом железе
* Хочется чтобы не хакнули
* Хочется чтобы было доступно онлайн 24/7/256
* Хочется чтобы код был хороший, джун за 3 месяца въехал, и синьоров уволить
* Хочется чтобы багов не было
И вот эти хотелки все одновременно не делаются. Это и есть основной архитектурной проблемой. Надо выбрать самые актуальные, и пожертвовать остальными.

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

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

А проблема с архитектурой в том, что выбирают в какой плоскости будем строить и если что-то пришло с другой плоскости это просто в хлам все разламывает.

INNER JOIN EXTRACT("Person", "Childrens.idx") c ON p.DocID = c.DocID

А не смотрел как аналогичная магия у постргеса сделана (json[b]_to_record[set] + LATERAL JOIN)? В твоем случае для указанной схемы было бы что-то а-ля

SELECT person.*
FROM 
  person,
  json_to_record(person.address) AS address(street string, house_numer integer),
  json_to_recordset(person.children) AS children(age integer, <...>)
WHERE ...
...
То есть если ты джойнишь таблицу с функцией возвращающей рекорд(сет), то пг автоматом делает латерал джойн, причем критерии джойна указывать не нужно — каждый «распакованный» рекорд по умолчанию джойнится с родительским (твое ON p.DocID = c.DocID с магическими полями). Как-то так это выглядит.

Мне удалось поработать с тремя базами данных из мира RDBMS.
Это Oracle, MS SQL и Postgres. И нада сказать что у Postgres самый не продуманный синтаксис. Какая-то смесь паскаля и SQL. Если от туда что-то заимствовать, то очень аккуратно.

Проапдейтил описание в теме.
Проблемы с проработкой синтаксиса вообще самые сложные. Нужно одновременно избежать «птичести» языка и оставить его лаконичным и простым для понимания.
Как видим, даже в больших компаниях случаются промашки. Но мы не большая компания, поэтому свободно можем вносить все свои пожелания в синтаксис :)
Ваши мнения ?

.idx как служебное слово еще и внутри строки в кавычках — это уже начинаются хаки.
Simple is preferable to complex, complex is preferable to complicated.

Почему хак ? При открытии файла, например, нужно передать путь к файлу. Здесь тоже самое, но путь к секции. Если так удобней, можно точку заменить слешем. Точку я просто позаимствовал из Монго. Можно голосовать, точка или слеш ? )

Ну в пути к файлу все папки переименовываются, а тут же хардкод-магия?

В каком смысле хардкод ? Это строка, строку можно передать любой. Вообще формат этого оператора в SQL следующий.
EXTRACT(CollectionName, PathToSection)

Вот кстате, есть еще более простой интерфейс. Тоже формируешь путь к секции и автоматом подсказывает какие атрибуты возможны в контексте. А потом и какие велью для этого атрибута.

youtu.be/3xl8XCOzUX8

Я вижу

«Childrens»: [
{
«Name»: «Bob»,
«Age»: 5
}

и не вижу

«Childrens.idx»

Откуда оно взялось?

Здесь idx служебное слово, которое говорит что мы спускаемся внутрь массива.
Другими словами, есть разница между
{
   "Childrens": { 
       "Age": 5 
   }
}
и
{
   "Childrens": [
      { 
         "Age": 5 
      }
   ]
}

По контексту, если путь выглядит так
Childrens.Idx.Age

То это означает массив, где вместо Idx может быть подставлен индекс в массиве, тоесть. Например если у Джона три ребенка, то к каждому из их Age можно доступится так.
Childrens.0.Age
Childrens.1.Age
Childrens.2.Age

Но поскольку мы не хотим какогото конкретного Children Джона, а хотим вернуть вообще всех чилдренов, то мы пишем Childrens.Idx.Age

Idx это единственное ключевое слово тут, оно эквивалент открытию квадратной скобки в джисоне.

Вот кстате запрос, который вернет все документы Person у которых нет детей

SELECT p.* 
FROM Person p
LEFT JOIN EXTRACT("Person", "Childrens.idx") c ON p.DocID = c.DocID
WHERE c.DocID IS NULL

А так, у когорых этих детей больше 2

SELECT p.Name 
FROM Person p
INNER JOIN EXTRACT("Person", "Childrens.idx") c ON p.DocID = c.DocID
GROUP BY c.DocID
HAVING COUNT(*) > 2

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

ну вот я ж говорю, чти это магия. и ни разу не интуитивна.
вот «children[]» было бы понятнее.
EXTRACT ARRAY еще проще, наверное

Хм. А как путь будет выглядеть ?
Childrens.[].Age ?

EXTRACT ARRAY

Тут есть проблема.

Например если у меня есть такой джисон с вложенностью

{
   "Childrens":
   [
        "Toys": [
                {"Car": "Red", "Price": 3},
                {"Car": "Black", "Price": 5},
         ]
   ]
}

То чтоб вытянуть таблицу с игрушками из документа мне нужно

SELECT * FROM EXTRACT("Person", "Childrens.idx.Toys.idx") Toys

А как будет с

EXTRACT ARRAY

?

Кто такой вообще

„Person”

?
Твоя штука напоминает конфиги. Я на прошлом проекте сделал парсилку для конфигов, которая хавает массивы:
dect.pincode=0000
dect.init_timer=1000000
#dect.intercom_melody=2
dect.hss[0].name=Kitchen1
dect.hss[1].name=Bathroom 2
dect.hss[2].name=WC 3
dect.hss[3].name=Corridoor 4
dect.hss[3].use_cadence=1
dect.hss[4].name=Miniroom
dect.hss[4].use_cadence=1
dect.hss[5].name=Megaroom
Тупо и работает.

Хм, а это мысль

SELECT * FROM EXTRACT("Person«, «Childrens[].Toys[]») Toys

Пожалуй поменяю синтаксис. Тем более здесь печатать 2 символа вместо трех, мелочь а удобней.

Потому что Person это коллекция.
Например в базе данных может быть две коллекции, Person и Company. В Person лежит 100 документов описывающих сотрудников. А в Company пять документов описывающих компании. Когда мы пишем FROM мы не знаем из какой коллекции делать выборку, поэтому нужно писать это Person или Company.
Можно и без EXTRACT
Но тогда мы сделаем выборку только атрибутов верхнего уровня (не вложенных)

SELECT * FROM Person
SELECT * FROM Company

select max(t) for t in c.toys for c in p.children for p in Persons
типа питон

Да, но нам нужен SQL )
В любом случае, мне кажется синтаксис получился более простым и лаконичным чем у Монго и Ms SQL при работе с джисонами. Если помню там несколько ключевых слов

docs.microsoft.com/...​ver?view=sql-server-ver15

А здесь получилось вообще без них, с одним приемом

Childrens.[].Age

select max(*) from children[].toys[].price
select max(price) from children[].toys[]
select max(toys[].price) from children[]

select max(*) from children[].toys[].price

Так не получится, потому что Person это коллекция. А children[].toys[].price это путь в каждом документе этой коллекции. Коллекция, аналог таблицы. А документ аналог строки в таблице. Тоесть когда указываем From нужно писать что является коллекцией для нас.

select max(*) from EXTRACT("Person«, «children[].toys[].price»)

Тю. Ну так
select max(price) from Person.children[].toys[]
или
select max(price) from Person/children[].toys[]
или
select max(price) from Person::children[].toys[]
или
select max(price) from Person->children[].toys[]

select max(price) from Person.children[].toys[]

Вот этот вариант не плох. Да, определенно нужно на нем остановится.

Хм, все оказалось не так сложно как я изначально думал. Выдалось мне сегодня часика три свободных и первая версия SQL даже както работает. Разбил запрос регекспом на лексемы.
С лексем построил аст и бросил выполнять в линку интерпретатор.
Спасибо наследию «днипро ди би», где линкью как-то был написан и даже достаточно толково, трафик не гоняет и выполняется на стороне сервера С++.

www.youtube.com/watch?v=mLLnkG2r59Y

Конечно это 20% работы из того что должно работать с SQL. Нужно прикрутить нормальные экспрешины, дистинкт, груп бай, джоины и посеять зачатки оптимизатора запросов, который необходим для эпических джоинов и групбай.
Но это уже будет другая история, на следующей неделе.

PS: Кстате оцените красоту игры. Я сделал предопределенную базу данных Мастер. Мастер хранит всю метаинформацию о сервере, на подобии того как это делает MS SQL, где можно вытягивать запросами информацию о таблицах, колонках, ключах, процедурах, статистике и др служебную информацию. Ну и в стиле функциональщины, Мастер хранит информацию также о себе самом.

Да, оттуда же позаимствована идея )
Красивое решение, когда база держит свою же мета информацию и позволяет ее же языком запросов искать информацию.
Это редкость, обычно городят свои кривые костыли в виде отдельного подвида комманд чтобы получить конфиги СУБД, список баз, коллекций и тд. По крайней мере для noSql

Да ребята, в противостоянии по удобству между SQL и Mongo, уверенно побеждает LINQ. Всё-таки древовидные структуры не очень хорошо ложатся на табличный SQL, а Монго синтаксис далёк от идеала.

Поскольку LINQ у меня уже есть (или его подобие) всё-таки будем докручивать чистый SQL по образцу TSQL/MSSQL. Буду думать как это сделать наиболее очевидным способом.

Так юзай мнго драйвер через LINQ, и EF для SQL.
Мы так и делаем, и ничего кроме LINQ у тебя на проекте не будет.

LINQ это часть орм которая используется в самом коде проекта. Его вотчина это код на сишарп или джава.

А речь про SQL который можно будет использовать аналитиком где нибудь в консоли для мониторинга базы.

Можно пытаться LINQ перенести в С++ и транслировать его прямо с консоли и я даже это делал, но делал только урезанную версию. Без лямбд. Если лямбды нести то уж лучше SQL, его знают больше девелоперов, его проще учить.
И он декларативный, что полезно для оптимизации запросов в будущем.

Рекомендую сначала почитать сюда github.com/...​src/backend/parser/gram.y
Затем попытаться понять, как это используется в коде базы
А потом — решать поддерживать SQL грамматику

Серьезный труд. Я конечно такое неосилю, да и у меня структуры данных не табличные для такого. И на скорость выполнения всеравно, поскольку это не основной язык доступа к данным, а для эпизодических запросов из консоли.
План написать Select Top From Where Group By, Order By. Inner Left Join. И несколько агрегирующих ф-ций типа Sum, Min, Max, Avg.
Это покроет 80% простых задач и можно в принципе закрыть эту ф-сть как достаточную для первой версии.

Для следующей можешь скопип****** всю папку с исходниками — на выходе получшь распаршенное дерево. Но там само дерево тоже месяц курить)

Якщо діалект SQL містить рекурсивні CTE, то дерева на таблиці кладуться просто, елегантно та ефективно.

dba.stackexchange.com/q/283782/7895

CTE нельзя назвать образцом простоты запросов. Редкий дба вот так сходу наберёт CTE без подглядываний. Но бегать по произвольной иерархии объектов это полезная вещь и ее нужно будет как-то обдумать и упростить.

Якраз пхати жсон в поле — це костиль і порушення вже першої нормальної форми. Я вже не кажу про кордебалет з екстрактами. Окремо скорботно помовчимо про ефективність джойнів по екстрагованим полям. По 100к записів в табличках — і опачьки.

СТЕ-дерева в цьому смислі абсолютно нормалізовані, максимально ефективно індексуються і чудово інкапсулюються в API-like сет з процедур. Ну, да, треба трошки напружитись і освоїти рекурсію. Але воно окупається. Це той самий випадок, коли можна увійти в рекурсію, а вийти вже не захочеться.

Якраз пхати жсон в поле — це костиль і порушення вже першої нормальної форми.

Так у меня NoSQL. Оригинальные данные вообще в кей\велью хранилище лежат :)
SQL это просто еще один язык, доступный для запросов.
Вообще список поддерживаемых языков сейчас такой:

1. Key\Value запросы — доступны только изнутри ядра.
2. DQL — основной язык запросов, доступный из вне, позволяет гибко возвращать документы. Например если бы мы запрашивали на DQL эту страницу, то можно было бы указать что нас интересует только первые три уровня вложенности комментариев.
3. LINQ — надстройка над DQL. Позволяет удобно оперировать данными на уровне ОРМ.
4. И наконец SQL который является надстройкой над LINQ и используется только для эпизодических аналитических запросов из консоли.
5. И будет еще пятый, не язык, не знаю как назвать. Но джаваскрипт фреймворк благодаря которому можно веб страницы напрямую натягивать на документы из базы.

Тоесть, на твоей предыдущей поделке можно поставить крест?

А может и на этой поставим крест. Если я увижу что можно сделать ещё лучше и увижу новые горизонты.

не так: и на этой тоже ты поставишь крест, но немного позже. :)

Любая сложная разработка проходит через серию прототипов. Через серию ошибок и их решений. А вот архитекторов которые думают что умнее других, не прошли весь этот путь за года, можно считать джунами. Это пыль, а не архитекторы. Они не смотрят в суть вещей, что критично для этой профессии.

прототип длительностью лет эдак в 8?
ну да ок, можешь себе позволить :)))

Именно это отличает меня от галерных разработок. Где ставятся сроки все написать за 6 месяцев или год. А исполнители обычные девелоперы которых удалось нанять. Они открывают книжку и выдают грабли которые я выдавал в первый год когда курил эту тему. А теперь я прошел через все грабли за несколько лет и ушел значительно дальше чем ты себе можешь представить.

тут только одна проблема: ты со своими велосипедами никому не нужен.
А галерные гребцы — нужны.

Гребцы то нужны. Но под руководством таких как ты. Как там мудрость гласит, лучше стадо баранов возглавляемых тигром, чем стадо тигров возглавляемых бараном ) Даже не видишь тенденций в технологиях, не открыт к новому, апатичен, тчательно избегаешь любых технических дискуссий. Типичный приспобленец. Вообщем не занимай время.

«Ма! Неси таблетки, деду опять плохо!» :-))))

Кстате Сережа я нашел подходящую тему для тебя dou.ua/...​rums/topic/34411/#2203299

Вроде как здесь тебе должно быть попроще. Матана сильно меньше чем в Индиане.
Давай седлай белого коня и заскакивай, там уже все приготовлено.
А то как-то рыбалка уныло идёт, один окунек попался, но совсем не жирненький.

Я польщен тем что ты выделил время для моего трудоустройства.... но мне и так неплохо платят, поэтому не трать свое ценное время на всякую фигню, давай матана побольше :)

Строитель ты так себе, видели ;)

Кстате сама работа с Json документами неплохо сделана в самом MS SQL

MS выбрал свой путь, Postgres свой, MySQL третий, AWS Redshift несмотря на fork postgress’a прилепили свое чудачество,
C другими не имел дело.
Так вот, субъективно
MS SQL самый лаконичный и реляционный подход, имхо понятно, просто и логично.
Postgress — самый функциональный, и самый запутанный.
MySQL самый недоделанный
Redshift — еволвятся, рано говорить, но ± среднее

Как по мне, то самое крутое это интеграция MS SQL с xslt, xpath, xquyery, обработке json доков такое не светит тем более на стороне баз.

Строитель ты так себе, видели ;)

Помни, ковчег строили любители. Титаник строили профессионалы © =)

Это да;) как и да что рост есть, нельзя отрицать очевидное.

Какой подход лучше выбрать ?

А что значит лучше? Лучше для кого?

С точки зрения реализации фреймворк по типу монги как мне кажется запилить (и развивать) будет чуть попроще:
— пайплайн исполнения описан явно — в виде заданной юзером последовательности независимых друг от друга преобразований
— DSL описания каждого отдельно взятого преобразования достаточно низкоуровневый, практически готовое AST (нечего парсить, ну условно разумеется)

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

слишком разная архитектура под капотом, толку от этой похожести около нуля...

Так в этом и прелесть SQL. Это декларативный язык. Ты пишешь запрос, но не говоришь КАК его выполнять, это отдается на откуп оптимизиатору запросов. Поэтому по хорошему для SQL не имеет значение как хранятся данные. Оптимизатор может полностью переиграть план выполнения по своему усмотрению.
Это первое.
А второе — SQL просто очень распространенный язык, дефакто стандарт. Новые языки люди учат достаточно тяжело.

Так в этом и прелесть SQL. Это декларативный язык. Ты пишешь запрос, но не говоришь КАК его выполнять, это отдается на откуп оптимизатору запросов... Оптимизатор может полностью переиграть план выполнения по своему усмотрению.

Это понятно. Но и запилить такое сложнее (особенно хорошо запилить). Я же писал про «чуть попроще»... :)

А второе — SQL просто очень распространенный язык, дефакто стандарт. Новые языки люди учат достаточно тяжело.

И это легко доказывается тем фактом, что в мейнстриме у нас всего 1-2 языка... Ой, стоп :) Ты же делаешь не поделку для тинейджеров, а инструмент для спецов. Для спецов синтаксис — это разве проблема? Это задачка на пару вечеров мануал почитать...

З.Ы. Я собственно не пытаюсь тебя отговаривать от сиквела. Просто ты критерии «лучше» не обозначил изначально...

Кстати, а что у тебя было бы на выходе оптимайзера. Ну то есть понятно что какой-то план исполнения, но что бы представляло собой его внутреннее представление? Не вариации ли часом на тему монгового пайплайна — с линейной последовательностью операций описанных на каком-то внутреннем языке? Мне вот кажется что да... То есть начав с реализации такого фреймворка (и выставив его интерфейс наружу для старта) ты по сути реализуешь нечто, что все равно бы понадобилось даже если пойти по пути прикрутки полноценного сиквела... Или нет?

Да, были бы теже пайплайны.
Но использовать сами пайплайны как первый уровень абстракции это:
1. Усложнить жизнь пользователю задачей как правильно построить пайплайн.
2. Ввести ограничение на план выполнения запроса. Поле деятельности оптимизатора, который может совершенствоваться в дальнейшем и действовать на основе разной статистики, теперь сужается. Пользователь теперь должен сам вручную решать как правильно переписать запрос под новые условия.

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

Насколько наличие оптимизации критично для сценария который ты в самом начале упомянул — аналитики?

При всей безобразности синтаксиса пайплайнов а-ля монга у них есть имхо жирный плюс — это композиция. Каждый шаг можно и нужно анализировать отдельно, и даже многословные запросы достаточно легко «распутать» пошагово. Но их и писать можно так же — декомпозировать задачу на части и запилить пошагово. Возможно будет не оптимально, но повторюсь — так ли это важно для аналитики? Имхо требования к скиллу исполнителя (аналитика) даже снижаются — не нужен виртуоз, достаточно ремесленника осилившего синтаксис и понимающего что он хочет получить из имеющихся данных.

Посмотри примеры вот тут например www.practical-mongodb-aggregations.com/examples/examples.html — да, от синтаксиса кровь из глаз, но если на это не обращать внимания, то сами запросы легко разбирать. Именно потому что они как лего собраны из отдельных изолированных деталек.

www.practical-mongodb-aggregations.com/examples/examples.html

Хороший пример.

Монго

var pipeline = [
  // Match engineers only
  {"$match": {
    "vocation": "ENGINEER",
  }},
    
  // Sort by youngest person first
  {"$sort": {
    "dateofbirth": -1,
  }},      
    
  // Only include the first 3 youngest people
  {"$limit": 3},  

  // Exclude unrequired fields from each person record
  {"$unset": [
    "_id",
    "vocation",
    "address",
  ]},    
];

И его аналог в sql

SELECT TOP 3 * 
FROM Person 
WHERE vocation = 'Engineer'
ORDER BY dateofbirth DESC

Ты потерял последнюю клаузу, которая в переводе на человеческий язык звучит как «вернуть все поля документа кроме А, Б, В». Как ты это напишешь на сиквеле можно посмотреть?

Это кстате интересный вопрос.
В sql требуют перечислить колонки которые нужно вернуть. Более того, в нормальной конторе везде, где будет торчать звездочка в селекте, попросят явно указать список колонок. Это нужно для обратной совместимости. Если колонка добавится, то запрос который это читает и десериализирует точно не упадет. В Монго получается пошли своим путем. Ввели неочевидный стейтмент и открыли лазейку для таких ошибок в репозитории.

В Монго получается пошли своим путем. Ввели неочевидный стейтмент и открыли лазейку для таких ошибок в репозитории.

Мне кажется ты просто слегка предвзят, и потому из всех возможных объяснений выбираешь самое «конспирологическое» :) В монге у тебя нет схемы, в одной и той же коллекции могут быть документы разных версий с разными наборами полей (например). Так что в такой конструкции смысл вполне присутствует имхо... (з.ы. есишо я вообще неадекват зачеркнуто не адвокат монги, просто конкретно агрегейшн фреймворк имхо имеет свои плюсы по сравнению с сиквелом, а не только недостатки)...

Да, интересная дискуссия.
Вверху как Монго решила задачи из мира SQL
А вот как SQL решил задачи из мира Монго :)

docs.microsoft.com/...​ver?view=sql-server-ver15

В СlickHouse добавили EXCEPT модификатор чтоб исключить столбцы
clickhouse.tech/...​s/select/#except-modifier

Лично я, конечно, за sql-синтаксис, а то руками в консоле писать запросы к mongodb / elasticsearch... да собственно их и не пишут )

Добавил оператор EXCEPT. Это было не сложно.
www.youtube.com/watch?v=Ih_sjkzf6B0

Ушел думать как добавить Group By

использовать Nosql БД изначально плохое решение если речь не идет о хранении типа ключ-значение. Именно поэтому хайп вокруг Nosql уже несколько лет как затих.

Многие

Nosql

базы просто себя скомпремитировали. Таже Монго транзакции добавляла несколько лет. И добавила не в том виде в котором ожидалось. Как буд-то АСИД или транзакции хоть както противоречат документоориентированной модели.

К документу нужно относится как к доменной модели. У вас есть в приложении доменная модель, которая собирает данные с множества таблиц ? Если есть, то таблицы хороши только для аналитики, а для боевых запросов должны быть документы.

называйте как хотите. на больших данных вы по любому просядете на скорости.
Уже проходил это на одном проекте по складской логистике когда пришлось перекладывать базу с CouchDB на MSSQL. Причем перекладывать начали еще предыдущие
разрабы которые решили пойти по модному тренду и заюзать Nosql. а через несколько лет работы даже MSSQL пришлось оптимизировать вводить денормализацию и прочее. Nosql там даже не поднялся бы. Для наколенных поделок nosql может и сгодится но не для ентерпрайза. ну кроме как я писал ключ-значение — типа очереди сообщений и все такое

Есть разные СУБД.
ОЛТП работают на апдейты и не тяжёлое чтение.
ОЛАП на построение отчётов и выполнение аналитики.

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

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

Здесь два противоречащих друг другу предложения. Денормализация первый признак скатывания RDBMS в NoSql

Ну вот народ перебирал кучу баз — можешь глянуть, на что ориентировались при решении
medium.com/...​ics-database-11742643df34

ОЛТП работают на апдейты и не тяжёлое чтение.
ОЛАП на построение отчётов и выполнение аналитики.

У нас была для OLTP MongoDB
И SQL Server для OLAP
Такие костыли приходилось писать, когда данные из 2-х база надо было брать.
Лучше уже одну базу использовать.

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

Миграция отдельная жопа, постоянно аномалии в жанре в одной есть, в другой нет.

У тебя девопсы опять кнопку забрали? :-))

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

Кактус то раздаст вай фай, а вот твои поделки так и останутся поделками :-)

SQL синтаксис прост для понимания типичных запросов. Абсолютное большинство которых сводится к SELECT... WHERE..., грубо говоря, CRUD — наше всё.

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

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

Если кратко — базы данных это рыночная ниша. Изучать тебе нужно рынок, кому ты сможешь продать продукт. Мой совет — целиться в низкорентабельные рынки, потому что на высокорентабельных высоки и риски, тебя туда не впустят без существующей популярности на нижних уровнях. Потому, не гонись сразу делать корпоративные хотелки (к слову, корпорации часто сами не знают, чего хотят). Подумай, что ты можешь дать, чего ещё нет.

SQL же ты всегда можешь держать как транслируемый язык, но самой базе лучше на нём не работать, от слова «совсем». Нужен либо объектно-ориентированный язык, либо просто процедурный, но со строгой объектной типизацией (не обязательно сложной иерархической).

Узким местом сейчас является именно трансляция данных. Да, она быстрая. Но её много. Её оооооооочень много. И практически вся она — ненужная, тупо «перекладывание бумажек», если брать аналогию с человеческой работой. Если сможешь эту идею продать, это хорошая ниша, большая. HighLoad задачи возникают повсеместно.

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

Узким местом является добавление и удаление данных. Для бинарного дерева это тяжело. Для синтетических движков, стремящихся покрыть все потребности — это ад в случае сбоя, и слабые (часто нулевые) возможности контроля сбоя. То есть базе данных нужен достаточно серьёзный сборщик мусора. Добавь к этому хранение данных на SSD, поймёшь что сборка мусора может добавить проблем, если она под SSD не оптимизирована. В идеале — если твой софт сможет работать без файловой системы, а просто получая под свои задачи чистый массив — возможно, кстати, и с битыми ячейками.

Узким местом является хранение больших данных. Что такое HDD c черепичной записью — можно и не знать. А можно наоборот, взять эту нишу, используя HDD как помощь при хранении. Когда редкие данные будут улетать туда, и всё это — почти автоматически.

Широким местом является суточный ритм и недельный ритм. Всегда есть время, когда нагрузка на базу низка. На этом можно выигрывать, вводя циклы обслуживания. В частности, для просчёта избыточности данных, для проверки целостности, для дефрагментации, для перемещения в более дешёвое хранилище.

Хранение данных не просто рынок, а огромный рынок. На котором долгое время беспредельничают монополисты, делающие совсем не то, «что хотел пользователь». А основная тактика создателей баз данных — это спрятать голову в песок, и если проблему не собираются решать прямо сейчас — просто делать вид что её не существует, либо что это не их, а ваши проблемы.

Общение с движком данных — это тоже рынок. SQL устарел, это монстр. Для ручного запроса хорош, для гигатонн автоматического обмена инфой — что собаке пятая нога. Потому над ним и стоят куча фреймворков в языках программирования. И тебе ничто не помешает написать под них драйвер, в том числе под частичное хранение, когда часть данных лежит совсем в другом месте.

Поддержание многих копий данных — это переоценённый рынок. Базы данных, влезая в реализацию механизма, делают просто катастрофой локальное хранение. И вместо того чтобы строить нормальные машины (и кластера) в железе и хранить данные максимально близко и компактно, обмениваясь на скорости оперативной памяти — делают монстры синхронизации. Когда сами данные по своей природе практически всегда можно разделить, разбросать, сложно найти такие объекты, которые нуждаются в целостности всего и сразу. Настоящий обмен данных должен быть по принципу master-slave, то есть какая-то копия должна быть всегда основной, а какой-то — допустимы устаревшие данные. Затачиваться под производительность полного синхрона (резко снижая надёжность из-за транзакционности логов) — не нужно. Сама операция записи — редкая. Да, какая-то часть клиентов нуждается в сложных механизмах синхронизации, но их очень мало, лучше проигнорировать их как долю рынка — вместо этого удовлетворив самую основную массу, которой хватает простых (но быстрых, и главное, надёжных) механизмов.

Фанатеть же по SQL ты можешь сколько угодно. Но это язык. Он для тебя, для человека. Для машины нужен другой. И когда ты напишешь запрос EXPLAIN, на выходе ты должен увидеть совсем не SQL, а очень даже программный код. Тоже на языке, но очень близком к машинным кодам, как С. С циклами, возможно даже с механизмами транзакционности, с выделением памяти, с ограничениями количества. И на чём-то подобном иметь возможность отдать команду (но уже с каким-то синтаксическим сахаром).

В базах данных очень не хватает обычных команд, вроде «возьми там», «присвой сюда», «проверь условие». Существующие извраты с написанием этого в запрос — просто свиньи, сжирающие время, и генераторы ошибок. Читабельность кода, такая чтобы ошибки были видны — близка к нулю. А меж тем, нужны целые библиотеки подобного счастья, в том числе высокоспециализированные.

В базах данных весьма важна последовательность действий. Для человека она естественна. А для баз данных кто-то когда-то заложил идею, что всё воспринимается параллельным и надо «качественно» задавать запрос — что такая игрушка как «курсоры» — разумеется осталась, просто стала фактически недоступной разработчику, а если и доступной, то крайне тяжёлой с точки зрения поддержания транзакционности. Да, это может повлечь проблемы (вернее нюансы), что пока обрабатываются одни данные, прилетят другие, и к примеру, запрос SELECT не будет содержать точных данных на какой-то выбранный момент времени (пока выполнялся над другими строками, данные могли поменяться), и что ещё важнее, запрос UPDATE может что-то прогавить, если данные, влияющие на условие, в этот момент менялись. Но ценностью будет последовательность, когда можно быть уверенным, просто читая код, что на самом деле произойдёт. Когда можно самому сказать, что последовательность к примеру задаст такой-то индекс (уже построенный по условию WHERE). Бюрократы-теоретики естественно начнут волать про нормальные формы, про принципы — но правда в том, что естественное хранение данных — избыточно. Что индексы и служат по сути обходом нормализации (и нередко занимают места больше, чем сами данные). А последовательность обработки кардинально ускорит написание кода и снизит количество ошибок в нём.

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

------------------

Простой пример: пагинация на сайте. Крайне часто задействованный механизм. Есть, грубо говоря, 1000 страниц результата поиска, пользователь просматривает 9-ю. Логично же предусмотреть в запросе, требующим для этого какой-то выборки и сортировки, НЕ ДЕЛАТЬ полную сортировку, а выполнять именно выборку, пока выбирается. Логично же предусмотреть не делать полную выборку, а делать выборку только ключей. Логично же знать, какое поле позволяет по нему отсортировать, а какое — не имеет индекса. Логично же сохранить сам запрос как объект, дать ему условно говоря UID, и далее пользоваться уже предварительно сохранёнными данными. И что самое логичное — это НИЧЕГО из этого не реализовывать на базе, но дать программисту ПАТТЕРНЫ, как это делать самостоятельно. Этим радикально снизив цену специалиста, которому не придётся учить 100500 команд, как сделать то или это, вместо того он просто скопипастит готовое решение — сам по коду увидев, как приспособить его под конкретный случай.

Если всё это делает Гугл для себя — то почему нельзя допустить, что базой данных может управлять ограниченно умный дурак? Который вместо 100500 команд знает элементарные несколько, знает лучшие практики, и только те, которые нужны. Но чтобы взять и начать пользоваться «прямо сейчас», достаточно знать лишь базовые команды и простой синтаксис.

Когда-то языком под эту нишу был, и сейчас остаётся, SQL. Но повторюсь, в нём есть тяжёлые наследственные проблемы, и главная из них — смешение команд и данных в одном запросе. Это порождает ошибки, в том числе сложно обнаруживаемые. Потому, для совместимости, SQL нужно оставить. Но он должен транслироваться (притом прозрачно для программиста) в настоящий процедурный язык. Декларативные же языки опасны тем же, чем и красивы — абстрактной моделью, которая хороша там, куда подходит для чтения человеком, но тяжела как только делаешь шаг вправо-влево.

Ещё пример: бэкап данных. Ты видел, какая жуть творится в SQL? Ну вот. А меж тем, есть же возможность сделать всё очень быстро и компактно, чётко разбив на данные и команды восстановления, снижая тем самым и цену сбоя, и время работы, притом радикально, в разы, а то и на порядок.

Совсем кратко: рынок огромен. Подумай, «чего_хотел_пользователь», и подумай, легко ли будет это реализовать. Возможно, именно твоя база выстрелит и станет мега-популярной.

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

Хранение данных не просто рынок, а огромный рынок. На котором долгое время беспредельничают монополисты, делающие совсем не то, «что хотел пользователь». А основная тактика создателей баз данных — это спрятать голову в песок, и если проблему не собираются решать прямо сейчас — просто делать вид что её не существует, либо что это не их, а ваши проблемы.

Общение с движком данных — это тоже рынок. SQL устарел, это монстр. Для ручного запроса хорош, для гигатонн автоматического обмена инфой — что собаке пятая нога. Потому над ним и стоят куча фреймворков в языках программирования. И тебе ничто не помешает написать под них драйвер, в том числе под частичное хранение, когда часть данных лежит совсем в другом месте.

Но сейчас пока не готов к большому обсуждению, оно просто съест все свободное время )

В данном случае SQL это просто привычный синтаксис над совсем не табличными данными. Его хочу прикрутить не как основной язык запросов по чтению/изменению данных, а как язык который могут использовать аналитики в консоле анализируя время от времени какие-то данные, возможно строя какие-то отчёты. Для эффективных селектов нужны данные в таблицах, причем желательно в поколоночных хранилищах типа вертики кликхауса, что конечно не мой кейс.

Логично. Обсуждать имеет смысл то, что хочешь делать ты. Я лишь затронул то, что может повлиять на выбор. Выбор — это всегда многа букав, вплоть до бигдаты.

По поводу поколоночных хранилищ — это тоже монстр, который хорош для одних задач, но просто неподъёмная свинья для других. Это узкоспециализированный рынок, в который без многоденег не ходи, потому что только представительские расходы и траты времени на общение с идиотами со сверхвысоким ЧСВ заставят тебя задуматься об уничтожении целых стран и народностей.

SQL, повторюсь, традиция. Для разовых и нечастых задач он няшечка. Для HighLoad — неподъёмный протухающий труп Годзилы. Эффективными должны быть не селекты. Эффективным должен быть уровень трансляции самых простейших хотелок. Эффективным должен быть уровень обработки последовательности, что за чем должно идти. Эффективным должен быть уровень читабельности — это кардинально сокращает время разработки и цену поддержания актуальности.

А вот аналитика — вообще отдельная задача. Под неё не грех строить просто таки огромные объекты внутри оперативной памяти. Отожрать пару гигабайт на запрос? Да на, подавись. Зато запрос выполнится за 5 секунд, а не за месяц. То есть под аналитику вообще всё своё. Хочешь этот рынок — работай с движком, а не с эффективностью хранения. Вплоть до того что будешь строить отдельные таблицы и дублировать часть данных — чисто под аналитику. И это правильно. В отличие от «нормальных форм».

То есть основной вопрос не в том, что можно сделать. А в том, что ты хочешь сделать и продать. Что легче продать будет.

PS. А про консоль забудь. Человек как устройство памяти — вообще никакушечка. Всё хорошее должен помнить компьютер. А потому, человек должен написать что-то, что сначала где-то запомнит, и только потом выполнит. А это уже не консоль, это какая-то морда, какие-то драйвера к существующим средствам аналитики. Консоль хороша, чтобы выполнить простые вещи, имея ограниченный доступ из любой точки планеты. На этом её преимущества исчерпаны. Для всего остального нужен софт, нужны протоколы. Под консоль очень дорого обходится обучение человека, а под софтину надо чтобы «садись, работай, по ходу разберёшься». И не только для начинающих, даже опытным такое надо. Всем.

Ладно, немножно заспойлерю.
1. База данных документоориентированая. В основе ее лежат коллекции и документы.
2. База поддерживает аспектную (или мультидименшион) парадигму для джисон документов. Если в Монго документ это просто джисон, то здесь это джисон + еще 21 дименшин из параллельных джисонов, которые описывают аспектную логику работы с основным документом. Какой-то дименшин отвечает за секурити, какой-то за валидацию, какой-то за логирование, какой-то за сохранение, за транзакции, за параллельную работу, за индексы, за трек историчности, за синхронизацию, за компрессию, за шифрование ... десятки их.
3. Мультидименшин позволяет стройно и очень гибко описывать сложнейшую логику, которая разделена на аспектные слои. Это самая большая идея со времен ООП.
4. В основе хранения данных лежит понятие сторадж, или движок для стораджа. Если в большинстве баз данных существует всего один движок для хранения или два, которые устанавливаются на старте, то здесь уже на взлете поддерживаются 4 стораджа. Каждая имплементация стораджа хороша для своих задач. Один сторадж для ин мемори, один для маленьких коллекций, один для работы с диском, один для капед коллекций, один для пагинации и тд тп. У каждого стораджа свой алгоритм хранения и доступа к данным.
5. Аспектный движок или Aspect Asymmetric Machine позволяет делать поистине удивительные вещи. Части одного и тогоже документа могут использовать сразу несколько видов сторадж движков и вообще хранится на разных машинах. Все это настраивается на уровне атрибутов документа, как и множество другой аспектной логики.
6. Полная поддержка транзакционности в трех ее ипостасиях. Глупый ундо механизм, привычные нам транзакции уровня снапшот, репитбл рид, рид коммитед. И наконец введение транзакций второго порядка (при соблюдении определенных условий). Это транзакции которые могут быть удалены из коллекции, даже если после этой транзакции уже прошли новые комиты.
7. Принцип — все базы под одной крышей. Больше нет зоопарку из редисов, кафок, монго и тд тп. Экономия на трафике, экономия на интеграции, экономия на отладке, повышене скорости работы. Каждая коллекция может использовать свой движок и свои настройки. Одна хранится на диске, вторая в памяти, третья использует трехуровневый кеш и тд. В самых спартанских сборках, можно разгонять до нескольких миллионов апдейтов/чтений документов в секунду. Для сильно нагруженных коллекций аспектной логикой, скорость может опускаться до десятков тысяч в секунду.
8. Больше никаких сервис бас, спагетти из посылок и принятия сообщений, хендлеров, адского дебага. Новая концепция — есть только документы, они синхронизируются между собой, как они синхронизируются меня не интересует я работаю с документом как со своей локальной закешированной копией, где бы она не находилась. Сбои ? Это не мои проблемы, моя версия документа восстановится самостоятельно как только будет налажена сеть.
9. Давай досвидания не только селекты, но и ОРМы (по крайней мере для большинства задач). Вводится новое понятие топологии баз данных, три уровня кольца. В первом кольце мастер базы данных/шарды/бекапы. Во втором слейв базы данных, или копии базы данных нужные для работы сервисов. В третьем, эпизодические базы данных. Слепки закешированные в браузере, в телефоне, в десктоп приложении и тд. На примере доу, не нужно обновлять сообщения, они прилетают в твой дом обьект браузера самостоятельно. А джаваскрипт работает с этим всем как с локальным стораджем не зная ниокаких рест, ормах, сервисбасах и прочьем.
10, Для запроса данных используется новый язык DQL (похожий на GraphQL но проще и адаптирован для документов).. Его особенность, мы описываем какие именно данные из документа мы хотим получить, на какую глубину опустится при вычитке атрибутов. Больше никаких «пришли мне по сети все» или «пришли мне текстовый файлик» или «собери мою доменную модель из 100500 таблиц». Запрашиваем и обновляем только то что нужно.Просто и лаконично.
11. Быстройдействие достигается на трех китах. Первый — это кей\велью абстракция. Второй — это тотальное многоуровневое кеширование. Третий — все обновления базы данных и чтения идут через DQL, тоесть все данные читаются и обновляются батчами и только то что нужно в данном контексте.
12. Собственно то о чем эта тема. Для прикручивания аналитики, предлагается прикрутить сбоку традиционный селект вместо «ноу хау» из мира Монги, агрегационного фреймворка

Идеи классные, но реализовать всё это вместе, с возможностью выдержать вал проблем, которые непременно возникнут на первых версиях — задача крайне тяжёлая. Продать это — сложнее раз в 100. То есть 100 нанятых тобой сэйлзов продадут 1 лицензию.

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

А вот с новым языком «тяжеловато будет» — не то слово. Для сдвигов такого уровня нужна большая компания. Которая будет доказывать применимость уже только самим фактом, что она большая, и типа 2 десятилетия над этим трудилась.

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

А что такое документ? Какова реальная полезная нагрузка, под что ты реально затачиваешься?

Документ это просто Json.

Например

{
   "Name": "John"
}

Dimension, это измерение к нему. Которое может описывать определенную логику с атрибутами документа. Например если Name Должен быть провалидирован на апдейтах,
то добавляем Validation дименшин и к нему новый джисон

{
   "Name": 
   {
        "IsRequired": true
   }
}

Также проявляется принцип наследования атрибутов, если в дименшине написано так

{
   "IsRequired": true,
   "Name": 
   {
        "IsRequired": false,
        "MinLen": 3
   }
}

То правило о наличии атрибутов распротраняется на весь документ.
А на Name это требование снимается и распространяется новое требование что не менее 3х символов.

Аспектной логикой покрыто множество разносторонней функциональности. Это координальное упрощение системы. Если в любой системе всю аспектную логику вынести под капот субд, то там останется очень тонкий слой кастомной логики.

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

Document = 0,
Index = 1, 
History = 2, 
Sync = 3,
ExtendedStorage = 4,
Validation = 5,
Security = 6,
Encryption = 7,
Handlers = 8,
Profiler = 9,
Log = 10,
Transaction = 11,
Parallel = 12,
OperationLog = 13,
Note = 14,
Learn = 15,
Version = 16,
Cache = 17,
Language = 18,
SelfTest = 19,
Undo = 20

Ну, я бы такое не купил, если бы мне так объяснили. Грубо говоря, ты просто заявляешь мифическую универсальность, и... промахиваешься по рынку. Да, может ты и сделаешь классную вещь, но кроме тебя ею сможет воспользоваться никто.

По поводу выноса логики в базу — ты забываешь, что логика — это как минимум 2 исхода. Им должны быть сопоставлены какие-то действия. Да, логику ты в базу впишешь, а как быть с действиями? Твоя база знает, чё делать дальше? Я скажу, чё. Дублировать логику в коде, чтобы не нарваться на твою.

Проблемы баз данных — не столько технические, сколько человеческие. И вот их-то ты не только не решил, а даже не обозначил. А ведь именно их тебе надо продать. Или же наоборот, принять решение не делать свою базу, а сделать плагин к существующей, и продать сначала его. А уже потом покупателям — более лучшее решение.

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

Классный приём — задать мне же мой же вопрос. Это я спросил, для каких данных вообще ты целишь продукт, кто твой покупатель.

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

на рынке «универсальных» продуктов ловить тебе
нечего

Это зависит от технологического превосходства самого продукта. Если грубо говоря твой плюс не большой, то тягаться действительно нет смысла. Ты малоизвестен, документации на тебя мало, стековерфлоу о тебе пустой.
А если, условно, твой продукт дает возможность выдать рабочее решение допустим уже за 2-3 дня, когда другие блеют что нам понадобится на это 2-3 месяца, то тут ты просто можешь идти на опережение. Выкатить готовый прототип для проекта клиента, а остальные будут просто видеть что клиент уже увидел почти готовый прототип того, на что он планировал потратить месяцы.
И так решения начнуть продаваться. Так будет появлятся позитивный опыт от использования у первых клиентов.

Клиент может бояться, что прототип никогда не станет production quality. По крайней мере, если там опытные технари сидят.
И при нестандартной модели фиг потом перелезешь на другую базу.
В результате имеем vendor lock-in на экспериментальный продукт. То есть, успех их бизнеса полностью зависит от успеха твоего. И от того, купила ли Аннушка масло.

В результате имеем vendor lock-in на экспериментальный продукт

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

Нет, осталось мигрировать весь код, ранее работавший со стандартными средствами (SQL базой). И этого кода уже за время попыток написания скопилось дофига. И так как ты не фанат ОРМ, то код надо переписывать прямо в коде, а не в прослойке. То есть — заново переколотить весь их код, чтобы переехать на твою базу. (или чтобы съехать с твоей базы)

Ну тоесть ты напишешь лучше продукт чем крупные тех компании? Самому не смешно? Такие продукты с миллионами человекочасов и тут дело не только в выжатых наносекундах в бенчмарках, дело еще в бизнесмодели, там куча крутых спецов как маркетологов так и продаванов, в добавок хренова туча контрактов и патентов...

Я там пост в соседней теме оставил. Немного сумбурный конечно, обнести бы мысли в одну статью, но думаю общую мысль ты поймёшь.

dou.ua/...​rums/topic/34411/#2205912

На сегодняшний день большие корпорации абсолютно безпомощны что-то координально сменить.

Ты немного не уловил момент, стадия случайных или неожиданных изобретений в IT давно прошла, теперь это уже давно поработилось бизнесом и гигантскими корпорациями. Даже если ты будешь гением и напишешь супер мощный язык/базу...то фронт энд все равно будут писать на богомерзком недо-жабоскрипте и только корпорации уровня майкрософт с ее экосистемой, может таки как-то присунуть туда что-то приличное типа тайпскрипт...
Вобщем, это бизнес, и он может разгромить любое изобретение, будь оно трижды круче и лучше

Ты немного не уловил момент, стадия случайных или неожиданных изобретений в IT давно прошла, теперь это уже давно поработилось бизнесом и гигантскими корпорациями

Ну ты тоже не улавливаешь что живёшь а эпоху перемен. Тотже кликхаус был написан по существу одним человеком, пускай в застенках Яндекса. Но ничего не мешало написать и без Яндекса. Та же Монго, молодая база, начинала как стартап. Та же квест дб примеров море. Это все молодые продукты и каждый год появляется новое. Потому что рынок тонет в болоте, зарплаты ростут потому что разработка стала неоправданно сложной. И скоро мы придем очередной раз в точку когда все перепишут и построят по новой. АйТи развивается циклически.

Если твое изобретение сможет стать бизнесом тогда у него будет право на жизнь. Если нет перспективы стать бизнесом, то вероятность его существования околонулевая, ну или же бизнес из него сделает кто-то другой.

Друге правило бізнесу — якщо товар не продаси ти, його продасть хтось інший.

Клиент может бояться, что прототип никогда не станет production quality.

Клиент может бояться, что если глюков в нём хотя бы четверть от того, что он уже использует — то это пидзец, но здесь-то уже проложены пути их решения и есть персонал, который быстро порешает, а там — ОН ОДИН будет за всё отвечать, НИЧЕГО не в силах ни сделать, ни ответить, ни перестраховать риск. И только репутация продавца, что клиент купил не абы что, а проверенное решение от Гугл-сайз-кампани — будет служить ему оправданием.

И вы никогда не сможете изменить факта, что уровни иерархии не молодеют по мере роста, а только умирают и деградируют. Зона роста — всегда внизу. И только там делаются все настоящие дела предприятия. А наверху — только страх, зависть и жадность. Притом страх сильнее.

Повторюсь: на рынке, где по сей день покупают SAP решения, никто не посмотрит на твой стековерфлоу. Посмотрят на каком вертолёте ты прилетел, не старомоден ли он, не прошлого ли сезона.

Либо ты готов работать с узкой нишей, прокалывая барьеры, либо ты пробиваешь головой стену, за которой стоит ещё стена, за которой стоит ещё стена, за которой стоит, улыбаясь Кафка.

А если, условно, твой продукт дает возможность выдать рабочее решение допустим уже за 2-3 дня, когда другие блеют что нам понадобится на это 2-3 месяца, то тут ты просто можешь идти на опережение. Выкатить готовый прототип для проекта клиента, а остальные будут просто видеть что клиент уже увидел почти готовый прототип того, на что он планировал потратить месяцы.

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

— Ребята, вот вы пилите свой продукт, смотрите, я с помощью своей библиотеки и самопальной базы сделал прототип за 2 дня.
— Сори, мы рассматриваем только решения, которые работали в продакшене у BigTech не менее 5 лет на скейле хотя бы 100Х от скейла, который будет у нас. У нас нет денег и времени на эксперименты.

Чтоб достичь успеха, нужно долго и упорно пилить свой продукт, отвоевывая рынок кастомером за кастомером у больших игроков, обыгрывая их в фичах, безопасности, скорости, стабильности и надежности, набирая список showcase кастомеров, которым можно тыкнуть, когда кто-то спросит «где эта поделка будет использоваться кроме моего приложения?»
Или попасть внутрь BigTech на лидирующую должность, где ты можешь просто заставить всех юзать свою поделку. Там или она отвалится из-за ненужности, или случайно станет успешной, и тогда можно будет ее популяризовать за пределами компании, и с рекламой проблем будет меньше — «Смотрите, Microsoft использует эту штуку у себя внутри! Давайте и у вас попробуем.»

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

У меня нет планов в одиночку доходить до продакшина. Это сделать невозможно, тем более не уволившись на N лет с основной работы. Мой план сделать вполне рабочий прототип, написать достаточно статей \ презентаций \ документации + бенчмарки и пробовать искать инвестирование.

Или попасть внутрь BigTech на лидирующую должность, где ты можешь просто заставить всех юзать свою поделку.

И тогда твой продукт будет полностью принадлежать BigTech компании. А сама разработка стопорится внутренними подковерными перетурбациями.

У меня нет планов в одиночку доходить до продакшина. Это сделать невозможно, тем более не уволившись на N лет с основной работы. Мой план сделать вполне рабочий прототип, написать достаточно статей \ презентаций \ документации + бенчмарки

Тебе мало одного заброшенного репозитория на гитхабе, в котором пасутся в лучшем случае фрики? Хочется еще один?

пробовать искать инвестирование

Ага, инвестирование однозначно дадут человеку, который считает продукт своим хобби и настолько не уверен в нем, что предпочитает продолжать грести на галере, а не уделять продукту 100% времени. Или продукту с 0 кастомеров и 0 существенных showcase-ов.

И тогда твой продукт будет полностью принадлежать BigTech компании.

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

Ты же все равно сохранишь за собой статус «Создатель XXXX», который легко конвертируется в плюшки для тебя лично.

А сама разработка стопорится внутренними подковерными перетурбациями.

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

Тебе мало одного заброшенного репозитория на гитхабе, в котором пасутся в лучшем случае фрики? Хочется еще один?

Нормальный репозиторий. В среднем по одному клону каждые два дня.

Ага, инвестирование однозначно дадут человеку, который считает продукт своим хобби и настолько не уверен в нем, что предпочитает продолжать грести на галере, а не уделять продукту 100% времени. Или продукту с 0 кастомеров и 0 существенных showcase-ов.

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

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

Здесь я не понял мысль. Соскочить с одной галеры и перескочить на другую (пускай и выше уровня), чтобы просто подарить свой продукт ? Потому что все, что ты будешь писать в стенах другой компании будет принадлежать другой компании, полностью.

Я тебе скажу по секрету, каким бы интересным ни был проект, через пару лет он заебывает и хочет чего-то нового.

За 8 лет мне он не надоел. Если основательно копать, то 2 года это очень мало. Тема большая.

Соскочить с одной галеры и перескочить на другую (пускай и выше уровня), чтобы просто подарить свой продукт ? Потому что все, что ты будешь писать в стенах другой компании будет принадлежать другой компании, полностью.

nginx тоже писался на галере, но лежит в опенсорсе. Да и всякие С и джавы, кажется, галерного производства.

Здесь я не понял мысль. Соскочить с одной галеры и перескочить на другую (пускай и выше уровня), чтобы просто подарить свой продукт ? Потому что все, что ты будешь писать в стенах другой компании будет принадлежать другой компании, полностью.

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

Я советую пилить свою идею на «галере высокого уровня», потому что это даст ей какой-то шанс быть реализованной. Условно говоря, идея сделать микропроцессор, возникшая внутри Intel — это бизнес объемом в триллионы долларов, которые будет жить, вероятно, сотню лет. Люди, которые были к ней причастны, давно не нуждаются в том, чтоб работать за деньги. Идея сделать микропроцессор, возникшая за пределами Intel в чьем-то воспаленном мозгу, примерно с 100% вероятностью умрет вместе с ее создателем, не принеся ни копейки ни создателю, ни обществу.

Ну вот здесь показателен, как мне кажется, конфликт в свое время Эдисона и Теслы. Тесла был выскочкой, без продукта и пришел работать в фаанг Эдисона. На тот момент Эдисон был уже известным изобретателем, промышленником и нанимал людей по всей америке. Случился конфликт интересов. Эдисон продвигал постоянный ток, у Теслы были свои взгляды, будущее за переменным током. И что же произошло. Только когда Тесла уволился и организовал свое дело, удалось изменить ход истории. В фаанг мне дорога закрыта, меня там съедят живьем. Даже не из-за технической части, а из-за постоянной подковерной борьбы в большой корпорации. Я хочу переписать чуть ли не половину того что они наворотили. Начиная от основы из основ — поисковых алгоритмов, заканчивая правильными СУБД, которые как фундамент в здании задают тон того, как на самом деле должно строится приложение.

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

Это лечится втечение первых 6 месяцев, можешь мне поверить.

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

Да задача не простая. Но много кода уже написано. Через месяца 2-3 если все Ок,
буду подбирать уже проект на котором можно обкатать саму субд и вычистить основной скоп ошибок в коде.

Кстате у кого какие идеи ? Вариантов море, от переписать сайт доу с его древовидными комментариями до социальных сетей или даже мессенджера :)

Задача № 0 — выявить, в кого ты целишься. Кто покупатель. Соответственно найти, кто и с кем соревнуется в его нише, и попробовать себя там. Лучше в открытых проектах, где успех заметят некоторые, но реальную глубину успеха будешь знать только ты, как и реальную цену ошибок — вовремя исправив которые, ты убережёшь свою репутацию от плохой кармы.

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

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

К сожалению, мне нечего показать.
Но, почившая "днипро ди би"© которая являлась простеньким пруф оф концепт зарекомендовала себя неплохо. Например я ее отлаживал на проектировании небольшой соцсети, с лайками репостами друзьями лентой новостей и тд, где-то на 50 таблиц/сущностей. Так вот, весь DAL, весь класс репозиториев, я помню написал за полтора дня.
Вот эпизод с этого репозитория.

    public class DAL
    {
        public User CreateDefaultUser()
        {
            User user = new User();
            user.Author = new Author();
            user.Author.IsActive = true;
            user.SecKey = Guid.NewGuid().ToString("N");
            user.Groups.Add(new Group() { Name = "Media",
                                          Author = new Author { IsActive = true },
                                          Robots = { new Robot { Author = new Author { IsActive = true },
                                                                 Name = "Coub",
                                                                 Source = new Source { Type = "All" },
                                                                 Conditions = { new Condition { Phrase = "" } },
                                                                 Output = new Output {Type = "coub" } },
                                                     new Robot { Author = new Author { IsActive = true },
                                                                 Name = "Gif",
                                                                 Source = new Source { Type = "All" },
                                                                 Conditions = { new Condition { Phrase = "" } },
                                                                 Output = new Output {Type = "gif" } },
                                                     new Robot { Author = new Author { IsActive = true },
                                                                 Name = "Video",
                                                                 Source = new Source { Type = "All" },
                                                                 Conditions = { new Condition { Phrase = "" } },
                                                                 Output = new Output {Type = "video" } } } });
            
            return user;
        }

        public void AddUser(User user)
        {
            user.Validate();

            DB.AddDoc(user);
        }

       
        public void AddNotification(string login, string groupName, Notification notification)
        {
            notification.Validate();

            DB.GetWhere(new User { Login = login, Groups = { new Group { Name = groupName } } })
              .Insert(new User { Groups = { new Group { Notifications = { notification } } } },
                      Change.CreateOne(notification, ChangeEnum.Add));

            this.SaveUserFileLog(login);
        }

        #region Comment

        public void LikeRobotComment(string login, string groupName, string robotName, DateTime commentDate, Like like)
        {
            like.Validate();

            DB.GetWhere(new User
            {
                Login = login,
                Groups = { new Group { Name = groupName,
                                       Robots = { new Robot { Name = robotName,
                                                              Comments = { new Comment { Author = new Author { OnDate = commentDate } } } } } } }
            })
            .Insert(new User { Groups = { new Group { Robots = { new Robot { Comments = { new Comment { Author = new Author { Likes = { like } } } } } } } } },
                    Change.CreateOne(like, ChangeEnum.Add));


            AddEvent(Event.Create(login, "Comment", login, groupName, robotName, null, commentDate,
                                  "NewLike", like.Login, string.Format("Your comment has new like from {0}.", like.Login)));

            this.SaveUserFileLog(login);
        }

        

        public Comment GetLibraryComment(string login, string libName, DateTime? commentDate)
        {
            return DB.GetWhere(new User
            {
                Login = login,
                Libraries = { new Library { Name = libName,
                                            Comments = {new Comment {Author = new Author {OnDate = commentDate}}}}}
            })
            .Select<Comment>(new User { Libraries = { new Library { Comments = { new Comment() } } } });
        }

        #endregion

        #endregion

        #region Comments

        public void AddCommentToRobot(string login, string groupName, string robotName, Comment comment)
        {
            comment.Validate();

            //inert comment
            DB.GetWhere(new User { Login = login,
                                   Groups = { new Group { Name = groupName,
                                   Robots = { new Robot { Name = robotName } } } } })
                .Insert(new User { Groups = { new Group { Robots = { new Robot { Comments = { comment } } } } } },
                        Change.CreateOne(comment, ChangeEnum.Add));

            //create event
            Event ev = Event.Create(login, "Group", login, groupName, robotName, null, DateTime.MinValue,
                                    "NewComment", comment.Author.Login, string.Format("Your group {0} has new comment from {1}.", groupName, comment.Author.Login));

            //notify subscribers
            DB.GetWhere(new User
            {
                Login = login,
                Groups = { new Group { Name = groupName,
                                       Robots = {new Robot { Name = robotName,
                                                             Subscribers = { new Subscriber { IsActive = true } } } } } }
            })
            .Join(new User { Groups = { new Group { Robots = { new Robot { Subscribers = { new Subscriber { Login = "" } } } } } } }, //lambda
                  new User { Login = "" })
            .Insert(new User { Events = { ev } },
                    Change.CreateOne(ev, ChangeEnum.Add),
                    JoinedEnum.SecondTable);

            if (login != comment.Author.Login) //do not send to yourself
            {
                //notify me
                AddEvent(ev);
            }

            this.SaveUserFileLog(login);
        }

        public void AddCommentToLibrary(string login, string libName, Comment comment)
        {
            comment.Validate();

            //insert comment
            DB.GetWhere(new User { Login = login, Libraries = { new Library { Name = libName } } })
                .Insert(new User { Libraries = { new Library { Comments = { comment } } } },
                        Change.CreateOne(comment, ChangeEnum.Add));

            //create event
            Event ev = Event.Create(login, "Library", login, null, null, libName, DateTime.MinValue,
                                    "NewComment", comment.Author.Login, string.Format("Your library {0} has new comment from {1}.", libName, comment.Author.Login));

            //notify subscribers
            DB.GetWhere(new User { Login = login, Libraries = { new Library { Name = libName, Subscribers = { new Subscriber { IsActive = true } } } } })
                .Join(new User { Libraries = { new Library { Subscribers = { new Subscriber { Login = "" } } } } },
                      new User { Login = "" })
                .Insert(new User { Events = { ev } },
                        Change.CreateOne(ev, ChangeEnum.Add),
                        JoinedEnum.SecondTable);

            if (login != comment.Author.Login) //do not send to yourself
            {
                //notify me
                AddEvent(ev);
            }

            this.SaveUserFileLog(login);
        }

        #endregion

        #region Subscribers

        public void AddSubscriberToRobot(string login, string groupName, string robotName, Subscriber subscriber)
        {
            subscriber.Validate();

            DB.GetWhere(new User { Login = login,
                Groups = { new Group { Name = groupName,
                                                          Robots = { new Robot { Name = robotName } } } } })
                .Insert(new User { Groups = { new Group { Robots = { new Robot { Subscribers = { subscriber } } } } } },
                                   Change.CreateOne(subscriber, ChangeEnum.Add));

            this.SaveUserFileLog(login);
        }

        public void DeleteSubscriberFromRobot(string login, string groupName, string robotName, string myLogin)
        {
            DB.GetWhere(new User
            {
                Login = login,
                Groups = { new Group { Name = groupName,
                                       Robots = {new Robot { Name = robotName,
                                                             Subscribers = { new Subscriber { Login = myLogin } } } } } }
            }).Update(new User { Groups = { new Group { Robots = { new Robot { Subscribers = { new Subscriber { IsActive = false } } } } } } });

            this.SaveUserFileLog(login);
        }

        public void AddSubscriberToLibrary(string login, string libName, Subscriber subscriber)
        {
            subscriber.Validate();

            DB.GetWhere(new User { Login = login, Libraries = { new Library { Name = libName } } })
                .Insert(new User { Libraries = { new Library { Subscribers = { subscriber } } } },
                        Change.CreateOne(subscriber, ChangeEnum.Add));
                        
            this.SaveUserFileLog(login);
        }

        public void DeleteSubscriberFromLibrary(string login, string libName, string myLogin)
        {
            DB.GetWhere(new User
            {
                Login = login,
                Libraries = { new Library { Name = libName,
                                            Subscribers = { new Subscriber { Login = myLogin } } } }
            }).Update(new User { Libraries = { new Library { Subscribers = { new Subscriber { IsActive = false } } } } });

            this.SaveUserFileLog(login);
        }

        #endregion

        #region Friends

        public void AddFriend(string login, Friend friend)
        {
            DB.GetWhere(new User { Login = login })
              .Insert(new User { Friends = { friend } },
                      Change.CreateOne(friend, ChangeEnum.Add));

            this.SaveUserFileLog(login);
        }

        public void ApproveFriend(string login, string friendLogin)
        {
            DB.GetWhere(new User { Login = login, Friends = { new Friend { Login = friendLogin } } })
              .Update(new User { Friends = { new Friend { IsApproved = true } } });

            this.SaveUserFileLog(login);
        }

        public void DeleteFriend(string login, string friendLogin)
        {
            DB.GetWhere(new User { Login = login, Friends = { new Friend { Login = friendLogin } } })
              .Update(new User { Friends = { new Friend { IsActive = false } } });

            this.SaveUserFileLog(login);
        }

        #endregion

        public void MarkAllAsRead(string login)
        {
            DB.GetWhere(new User { Login = login, Events = { new Event { IsRead = false } } })
              .Update(new User { Events = { new Event { IsRead = true } } });

            this.SaveUserFileLog(login);
        }
}
Скорость работы тогда не мерял базы. Помню все скрины работали на уровне 25кадров в секунду, тоесть задержки на скринах нельзя было определить человеческим зрением. А синтетический бенчмарк по базе катал по несколько миллионов запросов в секунду.

Так это был старый подход. Аспектный тогда был еще не очевиден. Если бы у меня в руках была новая аспектная база, то я бы всю внутрянку соц сети, со всеми локализациями трехуровневым кешированием, логированием, секурити и админками, синхронизацией и офлайновыми клиентами на мобильные устройствах и тд тп, написал бы .... ну может за неделю. Ну пускай месяц. При этом я не хочу сказать что эта база была заточена как то под соцсеть. Помню простейший прототип форума с топиками и постами я написал в строк 30 кода. Большой скрин с каскадным редактированием настроек в транзакции, тоже был где-то в 30-50 строк.

public void DeleteSubscriberFromLibrary(string login, string libName, string myLogin)
Один раз показав эту строку, ты никому больше не докажешь квалификацию даже джуна-манки-тестера. А ещё в дизайнеры базы данных рвёшься. Могу миллион раз повторить: программирование — гуманитарная наука, пусть и с критериями точной. Твой продукт должен быть для людей в первую очередь. Если люди не поймут твою модель данных, продукт мёртв.

Сначала концепт, потом двигло. В противном случае цена обучения и цена первого опыта будут неподъёмным порогом входа. Даже куда более простые продукты на этом сломались. То что порог низкий — лишь иллюзия.

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

Так ребята, пора рассказать про российский кликхаус. Это чисто аналитическая бд с очень спартанским функционалом. В ней нет транзакций, на таблицах нет индексов (кроме одного кластерного), нет джоинов, нельзя даже обновлять данные за вменяемое время. Это абсолютно другая база, она никак не годится для 90% приложений которым годятся реляционки и документалки. Только для аналитики. И нашумевший лозунг «кликхаус не тормозит», конечно же не тормозит. Потому что ближайший конкурент это на Си читать файлики с диска ридером и не париться.

а singlestore (memsql)?

Эта годится, но она реляционная. Сравнивать реляционные и документированные это как сравнивать самолёты и пароходы. Вот вверху я специально привел пример репозитория на примерно 50 таблиц/сущностей. Вот сколько у меня бы заняло времени на реляционке это все написать ? И каково было быстродействие когда джойнились в селектах по 10 таблиц. Ну явно не полтора дня. Галерные эстимейты на такую задачу это пару месяцев на несколько человек

Может, тебе для документоориентированной вообще на питоне правила писать? Оно и читаться будет, и методы в явном виде на документе можно вызвать, которые ты аспектами называешь.

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

ну вот, например, заказ билетов в укрзализныци. какой там дом и документы?

Хороший пример. Давайте разбираться.
1. В первом кольце мастер база данных и ее реплики. В мастер хранятся документы. В этих документах коллекции с билетами и направлениями поездов.
2. Во втором кольце сервис базы данных, они выполняют роль копий мастер данных. Одни сервисы могут обновлять или добавлять билеты. Другие занимаются акциями и ценами. Третьи содержат инфу о движении составов. Четвертые настройку секурити кто что может менять в документах. Пятые админ инфу и тд. Все эти документы автоматом синхронизируются между собой. Синк это тоже аспект.
3. В третьем кольце пользовательские базы данных. Используются транзакции второго порядка. Это когда ты можешь «вычесть» билет из мастер базы и если закроешь вкладку браузера и не подтвердишь бронь, мастер автоматом вернёт билет в коллекцию «роллбек».

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

Апдейты присылают только батчами, не дёргают рест методы по одному.

А как с покупкой билетов? Если ты заплатил — билет должен быть твоим, даже если сервер упал. Иначе — несвязки в бухгалтерии. И нельзя, чтобы два человека забронировали одно место. А такие ситуации постоянно пытаются произойти, когда мест на поезд мало осталось.

Если ты заплатил — билет должен быть твоим, даже если сервер упал

Обычно платежный сервис это отдельная система, у тебя есть выход на платежный gateway. Синхронизация между внутренней системой и внешней платежной не может быть транзакционной. Поэтому существует понятие с карточными транзакциями Return. А для внутренней Роллбек, даже если данные уже закомичены (это то что я у себя хочу заимплементить, как одна из киллер фич, откат транзакции даже если она уже закомичена).

А такие ситуации постоянно пытаются произойти, когда мест на поезд мало осталось.

Для этого весь процесс делится на 2 части. Онлайн часть и офлайн часть. Например начать процесс бронирования ты можешь только физически запросив у сервера бронь на билет, ты должен быть в онлайне. И если ты не успеешь этот процесс закончить за условные 15 минут в офлайн режиме, то твоя бронь будет анулирована.

Все это делается опять же через слои аспектной логики (в данном случае Sync). Где в документе ты размечаешь, что это поле в документе 100% должно быть синхронизировано онлайн с сервером. А это поле может быть не синхронизировано, и аплоадится оно будет позже, по правилам оптимистической блокировки. Для этого сервер твои изменения в конце будет мержить как Гит мержит ветки с изменениями и определять конфликты.

Если в Монго документ это просто джисон, то здесь это джисон + еще 21 дименшин из параллельных джисонов, которые описывают аспектную логику работы с основным документом. Какой-то дименшин отвечает за секурити, какой-то за валидацию, какой-то за логирование, какой-то за сохранение, за транзакции, за параллельную работу, за индексы, за трек историчности, за синхронизацию, за компрессию, за шифрование ... Мультидименшин позволяет стройно и очень гибко описывать сложнейшую логику, которая разделена на аспектные слои

Скажи, а вот эти дополнительные (мета)документы несущие отдельные аспекты бизнес-логики — как это будет работать в случае распределенной системы? Их же на всех нодах держать нужно будет? Но если да то возникает интересный парадокс: у тебя не только сами данные, у тебя бизнес логика становится eventually consistent — что кагбе слегка нонсенс...

Все проблемы кто папа решаются отношениями Мастер Слейв. Источник истины всегда один. На передовой может стоять фронт база, например для кеширования. Мастер плотно работает с диском. Слейв плотно с ОЗУ. Если Слейв уходит в офлайн или сбой, он обновляет все свои документы и мета документы с мастера.

И сейчас будет афтершок, но. В данной архитектуре атрибуты одного и того же документа могут лежать на разных нодах. И также кешироваться в памяти, часто не запрашиваемые атрибуты документа лежат на диске, а часто используемые в ОЗУ. И это логично. Не все данные в документах одинаково полезны, ровно как и в таблицах. Конечно такая гибкость базам типа монго и кауч даже не снилась. Они работают с документом как с файликом на диске. Что достаточно примитивно.

Все проблемы кто папа решаются отношениями Мастер Слейв. Источник истины всегда один... Если Слейв уходит в офлайн или сбой, он обновляет все свои документы и мета документы с мастера.

Во-первых, так решаются не все проблемы (если в приоритете availability твоя архитектура не полетит). Во-вторых, у тебя секурити (надо полагать включая контроль доступа) — это отдельный аспект. Соответственно как минимум этот аспект должен быть продублирован на все реплики (включая реплики для чтения). И соответственно никуда не девается проблема консистентности. Или у тебя рид реплика прежде чем отдавать данные будет пинговать мастер каждый раз и спрашивать можно ли?

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

Во-первых, так решаются не все проблемы (если в приоритете availability твоя архитектура не полетит).

Почему не полетит ? Если посмотреть по сути, у нас есть CAP теорема, где ты выбираешь любые две буквы. И у нас есть данные, которые могут быть не консистентны на момент запроса если ты выбрал AP. Теперь в чем принципиальная разница в невалидных данных и в невалидной мета информации ? Ее нет.
Ситуация когда секурити в системе распространяется не моментально, а спустя короткое время — абсолютно нормальная. Более того, в системах где с этим секурити более мене бодро работают, структуры данных с секурити кешируют. А кешь это все таже временная неконсистентность и какой-то ресурс тебе может быть доступен какое-то короткое время после того как в мастер данных у тебя уже забрали права.

И у нас есть данные, которые могут быть не консистентны на момент запроса если ты выбрал AP

"Данные могут быть неконсистентны" — это только вторая часть истории. Первая часть истории (более важная) — это то что я при этом ожидаю высокой доступности системы, которую твоя архитектура с одним мастером не обеспечивает (потому что чтение какое-то время может и поживет, но запись отваливается сразу). Это же не вопрос «один слейв отпал», у тебя может отвалиться целый регион — внутри которого ноды друг друга прекрасно видят и в тру АР системах могли бы проголосовать за нового папу (или заюзать какую-то leaderless магию) и продолжать работу.

Я не говорю что это невозможно в твоей системе запилить (откуда мне знать?) — я говорю что при этом возникает интересный вопрос eventually consistent бизнес логики (данные — то само собой).

З.Ы. А откуда пошел фольклор про «выберите любые две буквы»? В реале же постановка задачи «чем пожертвовать в случае нетворк партишн» (то есть Р дано, нужно принести в жертву или А, или С).

Первая часть истории (более важная) — это то что я при этом ожидаю высокой доступности системы, которую твоя архитектура с одним мастером не обеспечивает (потому что чтение какое-то время может и поживет, но запись отваливается сразу). Это же не вопрос «один слейв отпал», у тебя может отвалиться целый регион — внутри которого ноды друг друга прекрасно видят и в тру АР системах могли бы проголосовать за нового папу (или заюзать какую-то leaderless магию) и продолжать работу.

Давайте разбираться, какие есть методы синхронизации между Мастером и Слейвом.

1. Master + Slave как shadow copy.
В этой схеме слейв ничего не пишет в мастер, он просто принимает данные из мастера и сохраняет как копия (например как фронтальный кеш или реплика)

2. Master + Slave который видит всегда Master в онлайн. В этой схеме слейв может как читать так и писать в мастер, но комитить свои данные может только в случае если доступен Мастер по сети.

3. Master + Slave в офлайне. Это случай когда Мастер для Слейва может быть какое-то время в офлайне и слейв может накапливать историю изменений. При выходе Мастера в онлайн Слейв пытается закомитить свои данные с резолвом конфликтов.

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

Это первое, второе. Мастер всегда есть, если Мастер выпал то может быть выбран среди оставшихся слейвов, но при этом мерж в конечном итоге будет идти по принципу Master и Slave в офлайне с резолвом всех возможных конфликтов. Данные всеравно должны будут слиты в центральную базу данных.

у тебя может отвалиться целый регион

Этот вопрос тоже продуман. Я вижу что часто в системах нужно отправлять клиента согласно ближайшему региону. Это или шардирование, или тот же слейв который время от времени скидывает свои данные в мастер. Мастер это центральная субд, единый источник истины

В реале же постановка задачи «чем пожертвовать в случае нетворк партишн» (то есть Р дано, нужно принести в жертву или А, или С).

Общая формулировка что в CAP возможна CA, CP, AP.

А караваны можно грабить?

Ты джва года ждал такую базу ?

идеального нет ничего но есть более подходящее решение под конкретную задачу. у ТС мало входящих данных для однозначного совета или прикидки в каком-то приближении. да и понятие SQL совсем разное. постгресс умеет часть из того что Вы описали но многие его боятся как ... и к нему так же есть вопросы и к noSQL есть вопросы... отличная БД это когда можно красиво выбрать все по egrep+sed+awk :)))))
а вообще вспоминается такая сущность как «записи» в паскале :)))))

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

да — именно так. Скорее все привыкли что сиквел == мускуль и при том есть большой контингент которы не знает про марию или перкану и что у оракла тоже SQL да свой (PL/SQL) но SQL.
и постгрес на самом деле хорош как альтернатива, он скорее более другой взгляд на SQL и наверное такой какой должен был бы быть. Хотя о вкусах фломастеров... вообще говорить о некой идеальной БД не получится т.к. по факту идеальная БД — это журналируемая ФС (и опять же их есть много разных и каждый будет до красноглазия хвалить свое). Максимально правильно — это есть задача—реализация и тут принцип «як у кума» не пройдет. А для «задача—реализация» нужен правильный архитектор и тот кто понимает с чем едят задачу и иногда связки из нескольких разных инструментов сильно лучше одного мегакомбаина.

Підписатись на коментарі