Хмм, интересно.
С учетом того что загрузка больше одной строки в grep не имеет смысл т.к. все регулярные выражения все равно однострочные, то это поведение не имеет большого смысла. Но объяснило бы результаты. Надо исходники посмотреть…
И (отвечая на не заданный здесь вопрос) я полностью согласен с Девидом Кантором в его утверждении, что BigData начинаются там, где уже невозможно весь набор данных поместить в память сервера. Для текущих конфигураций 2х-сокетных серверов это где-то в районе 3ТБ twitter.com/thekanter/status/559034352474914816
webkumo да, думаю у него был SSD. И да это был не очень большой объем данных, чтобы применять MapReduce и кластеры. О чем собственно вся статья и была написана.
Да, мы тоже думали, что будет сложно. Но оглядевшись по сторонам (https://github.com/intersystems-ru/) мы поняли, что у нас в сообществе достаточно «молодых и горячих», которые могут создать «пару-тройку» интересных проектов на хакатоне. Трюк был привести самых звезд на школу. И бюджет мероприятия это позволял.
А что произошло со старым добрым ITP отладчиком, который мы применяли Н-лет назад? XDB это реинкарнация на Eclipse старого ITP? Или это совсем другая (не DTS) команда?
С поддержкой отдельная заморочка — совершенно неочевидно куда писать о проблемах возникших прямо сейчас и видных на текущем экране навигатора/карт? Можно завести какой секретный свайп/гестуру, чтобы оно автоматом собирало скриншот, геолокацию, и переходило в режим посылки письма с описанием проблемы?
Много вопросов возникает к реализации метода Do («Почему она делает столько всего из одного места через такой странный интерфейс?»). Если бы я делал агрегатные функции на коллекциях, я бы выделил отдельные, нормально доступные функции Min, Max, Sum, Product, Average. Это предоставило бы более высокоуровневый интерфейс со строгой типизацией (насколько возможно). Вместо такого перегруженного интерфейса, без проверок и странным способом вернуть значение.
(Более того, такие агрегатные функции могли бы возвращать объект такой же коллекции, что позволяло бы применять их каскадным образом.)
Высокоуровневая идея всегда применяемая во всех надстройках Cache' — любой другой язык должен d bnjut транслироваться в INT-код (Cache' ObjectScript) и объектный код, вне зависимости от количества препроцессинга и кодогенерации. Будь это SQL, манифест инсталлятора или описание домена iKnow. В случае компиляции встроенного SQL работает макро-препроцессор (так получилось исторически), в большинстве других случаев работают методы генераторы.
(Вам не удастся встроиться в препроцессор, но генераторы, если надо, Вам помогут реализовать еще какой встроенный доменный язык для любой вашей цели. Степеней свободы достаточно)
Возвращаясь к SQL — кроме упомянутого уже выше и ниже встроенного SQL (embedded SQL), компилируемого препроцессором в низкоуровневый COS в момент компиляции MAC программы или класса, есть еще и динамический SQL, компилируемый в похожую программу в момент первого исполнения.
Если вам интересно посмотреть, то вот упрощенный пример:
1. Создадим персистентный класс
Class User.Habra.BankData Extends %Persistent
{
Property Amount As %Numeric;
Property Account As %String;
}
И для такого простого метода со встроенным SQL выражением:
ClassMethod InsertAsSQL(Value As %Numeric, AccountName As %String) As %Status
{
&sql(insert into User_Habra.BankData(account, amount) values (:AccountName,:Value))
return $$$OK
}
после компиляции получаем следующий сгенерированный код (Ctrl+Shift+V в Студии):
...
zInsertAsSQL(Value,AccountName) [ AccountName,SQLCODE,Value ] public { New %ROWCOUNT,%ROWID,%msg,SQLCODE
;---&sql(insert into User_Habra.BankData(account, amount) values (:AccountName,:Value))
;--- ** SQL PUBLIC Variables: %ROWCOUNT, %ROWID, %msg, AccountName, SQLCODE, Value
Do %0Fo
return 1 }
q
%0Fo try { n sqldata5d
if $zu(115,1)=1||('$TLEVEL&&($zu(115,1)=2)) { TSTART s sqldata5d=1 }
n %i
s %i(2)=$g(AccountName),%i(3)=$g(Value)
s %ROWID=##class(User.Habra.BankData).%SQLInsert(.%i,$c(0,0,0,0,0,0,0),,,'$g(sqldata5d)),%ROWCOUNT='SQLCODE
if $zu(115,1)=1,$g(sqldata5d) { TCOMMIT:SQLCODE'<0 TROLLBACK:SQLCODE<0 1 }
}
catch { d SQLRunTimeError^%apiSQL($ze,.SQLCODE,.%msg) if $zu(115,1)=1,$g(sqldata5d) { TROLLBACK 1 } }
quit // From %0Fo
...
%SQLInsert(%d,%check,%inssel,%vco,%tstart=1,%mv=0)
new bva,%ele,%itm,%key,%l,%n,%nc,%oper,%pos,%s,sqlcode,sn,subs,icol set %oper="INSERT",sqlcode=0,%l=$c(0,0,0)
if $a(%check,7) { new %diu merge %diu=%d }
if $d(%d(1)),'$zu(115,11) { if %d(1)'="" { set SQLCODE=-111,%msg=$$FormatMessage^%occMessages(,"%SQL.Filer","SQLFiler6",,"ID","User_Habra"_"."_"BankData") QUIT "" } kill %d(1) }
if '$a(%check),'..%SQLValidateFields(.sqlcode) { set SQLCODE=sqlcode QUIT "" }
do ..%SQLNormalizeFields()
if %tstart { TSTART:($zu(115,1)=1)||('$TLEVEL&&($zu(115,1)=2)) } set $zt="ERRORInsert"
if '$a(%check) {
do {
if $g(%vco)'="" { d @%vco quit:sqlcode<0 }
} while 0
if sqlcode<0 { set SQLCODE=sqlcode do ..%SQLEExit() QUIT "" } // A constraint failed
}
if '$d(%d(1)) { set %d(1)=$i(^User.Habra.BankDataD) } elseif %d(1)>$g(^User.Habra.BankDataD) { if $i(^User.Habra.BankDataD,$zabs(%d(1)-$g(^User.Habra.BankDataD))) {}} elseif $d(^User.Habra.BankDataD(%d(1))) { set SQLCODE=-119,%msg=$$FormatMessage^%occMessages(,"%SQL.Filer","SQLFiler33",,"ID",%d(1),"User_Habra"_"."_"BankData"_"."_"ID") do ..%SQLEExit() QUIT "" }
set:'($d(%d(4))#2) %d(4)=""
if '$a(%check,2) {
new %ls lock +^User.Habra.BankDataD(%d(1))#"E":$zu(115,4) set %ls=$s('$t:0,1:$case($SYSTEM.Lock.ReturnCode(),0:1,4:2,2:2,1:3,:""))
set:%ls=2 $e(%check,2)=$c(1) set:%ls=2&&($tlevel) %0CacheLock("User.Habra.BankData","E")=1 set:$case(%ls,1:1,2:1,:0) $e(%l)=$c(1)
if '%ls||(%ls=3) { set SQLCODE=-110,%msg=$$FormatMessage^%occMessages(,"%SQL.Filer",$s('%ls:"SQLFiler40",1:"SQLFiler55"),,%oper,"User_Habra"_"."_"BankData",$g(%d(1))) do ..%SQLEExit() QUIT "" }
}
set ^User.Habra.BankDataD(%d(1))=$lb($g(%d(4)),$g(%d(3)),$g(%d(2)))
lock:$a(%l) -^User.Habra.BankDataD(%d(1))#"E"
TCOMMIT:%tstart&&($zu(115,1)=1)
set SQLCODE=0
QUIT %d(1) // %SQLInsert
Я не представляю себе COS программиста, который будет в своем коде так заботиться об уровнях транзакции, блокировках, откатах и обработке ошибок. COS/M код, созданный человеком, будет значительно короче и проще, и не будет заботиться (скорее всего) о всех граничных условиях. Хотя и не факт, что будет более читабельным (вон смотрите, там даже комментарии иногда генерируются :) )
В любом случае, прошу запомнить — у нас нет интерпретации при работе с SQL, транслятор SQL генерирует COS код, и, при разборе запросов, применяет все полагающиеся оптимизирующие эвристики. SQL это не просто и борьба за улучшения в генерируемом коде происходит постоянная.
P.S.
Большая просьба к участникам дискуссии — давайте попытаемся не выходить за пределы компетенции, и, в любом случае, попытаемся вести себя уважительно.
Мне нравится тенденция, наблюдаемая у некоторых участников дискуссии, пойти и почитать документы про Cache' (что я всячески приветствую), и давайте все остальные вопросы, которые возникнут по результатам тестирования задавать нам по другим каналам (письмом или через социальные сети). С удовольствием ответим!
Вот результаты того-же самого на Haswell-ULT ноутбуке (в режиме экономии батареи) под Cache' 2015.2
DEVLATEST:12:22:59:USER>do ^gtmtests
for mytest1 34
for mytest2 31
for msrtest1 write 335
for msrtest1 read 152
for msrtest2 write 520
for msrtest2 read 185
for cleanUP 0
Да, в случае голой заливки данных (я не говорю про запросы), INSERT в CacheSQL всегда будет медленнее чем прямая манипуляция глобалом, т.к. добавляется некоторый объем дополнительной работы необходимый для эмуляции ACID. Программист, работающий с глобалом напрямую обычно игнорирует некоторую транзакционную строгость, выигрывая по скорости. Иногда, при первичной загрузке данных, или в монопольном режиме на это можно закрыть глаза.
(Ну т.е. нет, конечно, полного сохранения функциональности, COS/M программист обычно дяже не знает о таких деталях о которых должен заботиться транслятор SQL в Cache' для правильной поддержки транзакционной целостности в SQL)
Еще раз, относитесь к прямому доступу к глобалам в Cache'/GT.M как к «ассемблеру», а SQL/объектной обертке в Cache' как C/C++. Большие системы правильнее писать, применяя абстракции более высокого уровня, но иногда, на критичном участке, «можно и на ассемблере пописать».
Недавно проводил тестирование «для одного большого банка», где, конечно же, мы проверяли в очередной раз разницу между CacheSQL vs Direct Global Access. В 2015.1 разница, для того простого случая что мы смоделировали, в разы (x2-x3), для 2015.2 на примерно 15%.
Т.е. сейчас, в 2015.2, я легко пожертвую 15% ухудшения по сравнению с идеальным случаем на голом глобале, но буду использовать более высокий уровень абстракции. Ибо так проще в долговременном плане.
Со СМЭВом этим вы заинтриговали, коллега, который этим занимается сейчас в отпуске, но у меня было впечатление что эта адовая XML схема вообще без проблем использовалась ими в их инсталляциях HealthShare/Ensemble в России. Вернется из отпуска — посмотрю на то как это проецируется в Cache'. Интересно.
То есть я понимаю, что у БД есть страницы, а у хранилища ниже уровнем — блоки, но можно я об этом не буду думать?
Так в Cache' и не надо об этом думать: поточные поля (символьные или бинарные) автоматически при сохранении бьются на куски.
Но в этом случае мы уже оперируем не чистым иерархическим хранилищем/глобалами, описанными автором, а пользуемся надстройкой над ними в виде объектов/классов, предоставленными Cache', но в основе своей использующими те же самые глобалы.
Давайте, все же я попытаюсь вернуть дискуссию в правильное с методической точки зрения русло?
Моя позиция такова — с логической точки зрения разницы многомерного хранилища (глобалов), документной базы (JSON/BSON) и XML иерархии нет. Они все оперируют эквивалентным множеством моделей и данных.
(Что я попытался донести в своей презентации на RIT 2015 Cache'Conf — www.slideshare.net/TimurSafin/multimodel-database-cach )
Интересна позиция Mongo в этом отношении osehra.org/sites/default/files/NoSQLtheModernMUMPS.pdf
При разговоре с группой пользователей VistA они пытались донести до аудитории мысль, что Mongo/документные базы, это тоже самое, что и иерархические базы (MUMPS/Cache'). С чем я не могу не согласиться :)
Еще один важный аспект, в большинстве случае движки СУБД оперируют вариациями b-tree формата (когда они не SSTable/LSM, конечно). И Cache' с его b*-tree тут не сильно отличается, хотя и отличается в деталях, облегчающих множество операций с глобалами, упомянутых Сергеем inetstar в этих двух статьях. Детали формата b*-tree мы попытались раскрыть в двух презентациях на недавнем RIT (моей ретроспективной, упомянутой выше, и Сергея Шутова про внутренности собственно Cache' b*-tree. Как оказалось его презентации в онлайне нет, как только исправим ситуацию сообщу в эту ветку)
Хороший пример, имеет смысл, спасибо!
(Есть некоторые «стилистичекие» замечания к авторам _такого_ формата, но раз уж выросло такое...)
При десериализации такого в объекты я бы в нескольких местах пропускал лишние промежуточные конверты (например, smev:MessageData/smev:AppData, или AddressData/Address), но наверное я чего-то не понимаю, и они там действительно нужны.
В-любом случае, я пока не вижу как, даже при таком адовом дизайне схемы, мы упрёмся в 32 вложенных подындекса. Все же хотелось бы примера из жизни, который имел бы смысл.
P.S.
И да, для больших BLOB-ов всегда и везде придется разбивать блоки на подузлы. В этом нет ничего специального — везде есть верхний предел блока базы, или страницы в хранилище, или ограничения по длине примитива, которые приходится обходить разбивая на куски, например, по 32КБ.
Трюк, при работе с базами данных, минимизировать количество обращений к диску, при заданных параметрах файловой системы и дискового хранилища. Понятно, что ни в какой ОС, нет возможности прочитать 32МБ BLOB за одно обращение к IO. И, понятно, что на практике придется читать блоками определенного размера (приближенными, например, к размеру блока файловой-системы). Так вот, при большом количестве возможных вариантов оказывается что хранение BLOB-ов кусками по 32КБ вполне хороший вариант. :)
С учетом того что загрузка больше одной строки в grep не имеет смысл т.к. все регулярные выражения все равно однострочные, то это поведение не имеет большого смысла. Но объяснило бы результаты. Надо исходники посмотреть…
Даже, работая в Интеле, я предпочитал по его статьям изучать архитектуру Nehalem, даже и имея доступ к HAS/MAS.
Все что меньше по размеру — «не очень Big Data»
— никогда, ничего не отправлять в store с отладочной сборкой хоть одной из библиотек.
Даже если использование отладочной библиотеки покажется кому-то допустимым костылем, но на самом деле нет. Никогда
(Более того, такие агрегатные функции могли бы возвращать объект такой же коллекции, что позволяло бы применять их каскадным образом.)
Высокоуровневая идея всегда применяемая во всех надстройках Cache' — любой другой язык должен d bnjut транслироваться в INT-код (Cache' ObjectScript) и объектный код, вне зависимости от количества препроцессинга и кодогенерации. Будь это SQL, манифест инсталлятора или описание домена iKnow. В случае компиляции встроенного SQL работает макро-препроцессор (так получилось исторически), в большинстве других случаев работают методы генераторы.
(Вам не удастся встроиться в препроцессор, но генераторы, если надо, Вам помогут реализовать еще какой встроенный доменный язык для любой вашей цели. Степеней свободы достаточно)
Возвращаясь к SQL — кроме упомянутого уже выше и ниже встроенного SQL (embedded SQL), компилируемого препроцессором в низкоуровневый COS в момент компиляции MAC программы или класса, есть еще и динамический SQL, компилируемый в похожую программу в момент первого исполнения.
Если вам интересно посмотреть, то вот упрощенный пример:
1. Создадим персистентный класс
И для такого простого метода со встроенным SQL выражением:
после компиляции получаем следующий сгенерированный код (Ctrl+Shift+V в Студии):
Я не представляю себе COS программиста, который будет в своем коде так заботиться об уровнях транзакции, блокировках, откатах и обработке ошибок. COS/M код, созданный человеком, будет значительно короче и проще, и не будет заботиться (скорее всего) о всех граничных условиях. Хотя и не факт, что будет более читабельным (вон смотрите, там даже комментарии иногда генерируются :) )
В любом случае, прошу запомнить — у нас нет интерпретации при работе с SQL, транслятор SQL генерирует COS код, и, при разборе запросов, применяет все полагающиеся оптимизирующие эвристики. SQL это не просто и борьба за улучшения в генерируемом коде происходит постоянная.
P.S.
Большая просьба к участникам дискуссии — давайте попытаемся не выходить за пределы компетенции, и, в любом случае, попытаемся вести себя уважительно.
Мне нравится тенденция, наблюдаемая у некоторых участников дискуссии, пойти и почитать документы про Cache' (что я всячески приветствую), и давайте все остальные вопросы, которые возникнут по результатам тестирования задавать нам по другим каналам (письмом или через социальные сети). С удовольствием ответим!
Вот результаты того-же самого на Haswell-ULT ноутбуке (в режиме экономии батареи) под Cache' 2015.2
(Ну т.е. нет, конечно, полного сохранения функциональности, COS/M программист обычно дяже не знает о таких деталях о которых должен заботиться транслятор SQL в Cache' для правильной поддержки транзакционной целостности в SQL)
Еще раз, относитесь к прямому доступу к глобалам в Cache'/GT.M как к «ассемблеру», а SQL/объектной обертке в Cache' как C/C++. Большие системы правильнее писать, применяя абстракции более высокого уровня, но иногда, на критичном участке, «можно и на ассемблере пописать».
Т.е. сейчас, в 2015.2, я легко пожертвую 15% ухудшения по сравнению с идеальным случаем на голом глобале, но буду использовать более высокий уровень абстракции. Ибо так проще в долговременном плане.
Так в Cache' и не надо об этом думать: поточные поля (символьные или бинарные) автоматически при сохранении бьются на куски.
Но в этом случае мы уже оперируем не чистым иерархическим хранилищем/глобалами, описанными автором, а пользуемся надстройкой над ними в виде объектов/классов, предоставленными Cache', но в основе своей использующими те же самые глобалы.
Моя позиция такова — с логической точки зрения разницы многомерного хранилища (глобалов), документной базы (JSON/BSON) и XML иерархии нет. Они все оперируют эквивалентным множеством моделей и данных.
(Что я попытался донести в своей презентации на RIT 2015 Cache'Conf —
www.slideshare.net/TimurSafin/multimodel-database-cach )
Интересна позиция Mongo в этом отношении osehra.org/sites/default/files/NoSQLtheModernMUMPS.pdf
При разговоре с группой пользователей VistA они пытались донести до аудитории мысль, что Mongo/документные базы, это тоже самое, что и иерархические базы (MUMPS/Cache'). С чем я не могу не согласиться :)
Еще один важный аспект, в большинстве случае движки СУБД оперируют вариациями b-tree формата (когда они не SSTable/LSM, конечно). И Cache' с его b*-tree тут не сильно отличается, хотя и отличается в деталях, облегчающих множество операций с глобалами, упомянутых Сергеем inetstar в этих двух статьях. Детали формата b*-tree мы попытались раскрыть в двух презентациях на недавнем RIT (моей ретроспективной, упомянутой выше, и Сергея Шутова про внутренности собственно Cache' b*-tree. Как оказалось его презентации в онлайне нет, как только исправим ситуацию сообщу в эту ветку)
(Есть некоторые «стилистичекие» замечания к авторам _такого_ формата, но раз уж выросло такое...)
При десериализации такого в объекты я бы в нескольких местах пропускал лишние промежуточные конверты (например, smev:MessageData/smev:AppData, или AddressData/Address), но наверное я чего-то не понимаю, и они там действительно нужны.
В-любом случае, я пока не вижу как, даже при таком адовом дизайне схемы, мы упрёмся в 32 вложенных подындекса. Все же хотелось бы примера из жизни, который имел бы смысл.
P.S.
И да, для больших BLOB-ов всегда и везде придется разбивать блоки на подузлы. В этом нет ничего специального — везде есть верхний предел блока базы, или страницы в хранилище, или ограничения по длине примитива, которые приходится обходить разбивая на куски, например, по 32КБ.
Трюк, при работе с базами данных, минимизировать количество обращений к диску, при заданных параметрах файловой системы и дискового хранилища. Понятно, что ни в какой ОС, нет возможности прочитать 32МБ BLOB за одно обращение к IO. И, понятно, что на практике придется читать блоками определенного размера (приближенными, например, к размеру блока файловой-системы). Так вот, при большом количестве возможных вариантов оказывается что хранение BLOB-ов кусками по 32КБ вполне хороший вариант. :)