Так как на этом этапе вообще нет никакой сетевой активности. Исключительно пересылка память-память.
И где это сказано? Некоторый DB-клиенты умеют видя localhost переходить на shared memory и другие IPC. COPY TO PROGRAM — это не просто память-память. Это пайп, файловая система, ACL и сериализация. Если TCP/IP — это порядка 80/1500 оверхеда, то сериализованные данные — это может быть в разы. Я не зря упоминал исходник, там не shared memory, там OpenPipeStream.
Я не говорю, что идея в корне плоха. Вполне может быть, что в Linux localhost TCP гораздо медленнее пайпов. Я знаю, что так было когда-то давно, но не следил, насколько что-либо изменилось. Для меня логичным выглядит взять и сравнить. Но с реальными конкурентами. А вы взяли самое медленное решение и на все конструктивные предложения сыпятся обвинения, что де вас не читают.
Вы заголовок статьи хотя бы читали? Источник — PostgreSQL. Как он Вам данные в бинарном потоке для MS SQL предоставит?
Я не только прочитал, а предоставил вам и код, и инструкции, как реализовать обмен именно между Postgres и MSSQL. В .NET бинарный обмен идёт через абстракцию DbDataReader. У SSIS таких абстракций из коробки побольше и маппинги получше. Естественно он не передаёт бинарный поток напрямую, но он не будет конвертировать interger из Postgres в string, добавлять к нему разделитель, что бы потом распарсить его обратно в int SQL Server'а.
Или Вы даже не читали статью по Вашей же ссылке?
Used by external tools to upload a binary data stream. This option is not intended for use with tools such as SQL Server Management Studio, SQLCMD, OSQL, or data access application programming interfaces such as SQL Server Native Client.
Вы же понимаете, что процитированное вами я уже написал ранее:
Вы не можете вызвать эту команду из sqlcmd или какой-нибудь IDE, но она есть и активно используется в BCP, SSIS и т.п. Там нет никаких промежуточных файлов, можно заливать данные прямо в TDS без посредников.
Упоминая тот же sqlcmd, под IDE подразумевая SSMS, DataGrip и т.п., а "binary data stream" озвучив как "заливать данные прямо в TDS". Только я не могу понять, в чём загвоздка. Ваш bcp делает тоже самое. Только гоняет через текстовый формат, реконвертируя каждое значение
Перечитайте внимательно статью:
Зачем мне её перечитывать, если это ваша мания везде видеть BULK INSERT? Я его не использую. Заодно, чтобы укрепить свою уверенность, что bcp работает через BULK INSERT, попробуйте погонять его с опцией -n, когда данные идут в native-режиме, а не через csv/tsv-серилизацию. Я бы ещё предложил посмотреть профайлером и сниффером, но вряд ли у вас получится.
В Вашем случае при вызове SqlBulkCopy? Ну тогда Вы вообще не понимаете, как он работает. Почитайте внимательней то, что я писал выше.
Я то как раз понимаю. Поэтому в примере и стоит TableDirect. Который в зависимости от провайдера может реализовываться по-разному. Поскольку передаёт данные протокол, а не SELECT. Я могу коннектится к No-SQL базе, я могу использовать LOB-протокол в постгресе, где не будет никаких SELECT вообще. Те же протоколы логической репликации или LISTEN/NOTIFY. Данные есть, а SELECT'ов нет. Так что у вас очень базовый, примитивный взгляд на предмет.
Запас у меня больше, чем на порядок. При меньшем запасе переключился бы с ramfs на tmpfs. Разница в их производительности — 1-2%. Несущественно.
Я вообще не понимаю, зачем использовать древний rafms, когда tempfs=ramfs+swap+куча фич. Но вот когда заканчивается ram, то картина сильно меняется. Может, конечно, в вашем мире большие данные — это 366K записей по 12 байт, что еле-еле переваливает за 4М. В моей практике это хотя бы несколько терабайт на ноду. Но уж точно это не тот объём который помещается в RAM. Замените в своём generate_series 1K на 10K, 100K, 1000K и опубликуйте результаты, как я это сделал для dtexec. Посмотрим на динамику. Делов-то, несколько ноликов добавить.
Да. Это подробно было описано в статье. Невнимательно читали.
Опять вы со своей песней. Где там в вашей статьей, что dtexec нельзя использовать как простую CLI как и bcp, дав ей те же реквизиты и tempdb, как и bcp, потому что… добавьте цитату из вашей статьи.
bcp под Linux замечательно UTF-8 кушает.
Да, мой косяк, забыл что с 2016 начал поддерживать.
Вы просто логически задумайтесь над своими же определениями. Утилита bcp у вас работает на постгрес-сервере, использует под капотом BULK INSERT. А какой сервер выполняет этот BULK INSERT? Определённо @@SERVERNAME, другого просто нет. Но BULK INSERT может читать только из файла. А где этот файл на @@SERVERNAME? И в итоге вся ваша схема не сходится.
Я бы предложил от боевых действий и попытки защитить свои позиции любой ценой перейти к конструктивным. Найти реально эффективное решение. Со своей стороны стараюсь вам подкидывать конкретные варианты для экспериментов, с вашей… осталось решить как этим воспользоваться))
Не верите — просто включите профайлер на SQL Server и убедитесь.
Вы так часто упрекаете других в невнимательном чтении, но сами не очень следуете своим стандартам. Я вам выше уже говорил, что включал профайлер. Там нет никаких BULK INSERT. И в коде SqlBulkCopy тоже нет никаких BULK INSERT.
Я понял, вы вообще не в курсе как оно работает. Так сфокусировались на BULK INSERT, что не удосужились узнать про INSERT BULK.
Да да, если посмотреть чуть внимательнее, есть и такая команда. А заглянув в спецификацию TDS, указание на которую вы проигнорировали, в п.2.2.6.1 узнаете, что есть такой Bulk Load BCP, начинающийся с команды "INSERT BULK", за которым идут метаданные и строки в виде raw data (таком же, как идут в протокол из SELECT).
Вы не можете вызвать эту команду из sqlcmd или какой-нибудь IDE, но она есть и активно используется в BCP, SSIS и т.п. Там нет никаких промежуточных файлов, можно заливать данные прямо в TDS без посредников. Вот пример как это используется в SqlBulkCopy, которую я задействовал в скрипте.
COPY вообще никаких данных не передает клиенту, а SELECT — передает.
Сам SELECT ничего не передает. Можете код посмотреть, а можете поспекулировать с командами вроде SELECT INTO.
При любых раскладах передача данных через TCP/IP будет медленней, чем запись в кеш, где данные и останутся при использовании ramfs, вплоть до удаления файла.
Конечно медленней (RDMA и мелланоксы не будем трогать). А вам рассказать, по какому протоколу bcp будет передавать данные на @@SERVERNAME из вашего примера? Только перед тем, как отправить всё по тому же TCP/IP вы решили ещё добавить сериализацию в текст (это когда timestamp, например, начинает занимать в 3 раза больше места) и десериализацию из него.
И в итоге создали узкое место — память. Вы целиком сериализуете всю таблицу и засовываете в ramfs, скармливаете батч размером 10M записей bcp, который также вынужен всё это буферизовать. Расскажите, плиз, что произойдёт когда таблица будет не мизерной как в вашем примере, а "относительно большой"?
Зачем брать в качестве референса пример, который не устраивал?
Вообще то вы его взяли в качестве референса и сравнили с ним свою bcp-реализацию. Я взял его же в качестве референса и предложил альтернативный подход, дав данные для нормализации по референсному варианту. У вас сложности с проведением простой арифметической операции? Хотите я вам помогу?
Почему для сравнения Вы берете вообще неизвестно какую таблицу с неизвестным количеством строк, а не пример из статьи? Что с чем сравниваете?
Не неизвестно какую, а SELECT INTO из вашего generate_series, один-в-один. Это вы почему-то используете в задаче передачи данных суррогаты (с кардинально отличающимся EXPLAIN), я же делал приближённый к реальности вариант.
Это в разы медленней, чем 881 ms.
Мда… из всего сказанного вы заметили только числовое значение. Давайте проведём батл в AWS/Azure/etc по копированию терабайтной таблицы с текстом в Unicode. Когда ваш вариант уйдёт на дно в первые же секунды, а SSIS всё быстро перекачает.
Вы невнимательно читаете, то что Вам пишет оппонент.
Слишком много ограничений для продуктивного использования.
Сами почитайте.
Ни Kerberos авторизации, ни сторонних компонентов, ни каталога.
Я очень внимательно читаю, просто вы пишете весьма странные вещи. Я вам говорю о простой консольной утилите dtexec, которую можно использовать вместо bcp. А вы мне про отсутствие поддержки Kerberos и даёте ссылку на статью, что де Hadoop с HDFS не поддерживаются. И одновременно сами сетуете:
Причем, bcp для Linux до сих пор не умеет авторизоваться через Kerberos. Поэтому использовать его можно только указывая кредентиалы в командной строке.
То есть он у вас для "продуктивного использования подошёл", а к другой консольной утилите повышенные требования?
Просто модифицируйте свой код на C# так, чтобы он выполнял ровно ту же задачу, что описана в статье. Тогда сразу увидите, что он медленней варианта, предложенного в решении.
Что именно эта утилита не делает? Он быстро и эффективно передаёт таблицы любых размеров, с поддержкой Unicode, с поддержкой того же Kerberos. Там где ваше решение упадёт, это продолжить работать. Или суть именно в этом, что оно должно упасть?
Поймите простую вещь. Никакие сервисы или классы все равно не могут использовать какие-то секретные SQL команды.
Так это вы нам рассказываете, про секретные возможности bcp. Хотя те же самые пакеты 0x7 (Bulk Load), с которыми работает он, использует и SSIS. И они прекрасно документированы в спецификации протокола TDS. И с какой стати должна возникнуть разница, а также в какой момент bcp получил монополию, — пока не объяснили.
При чем тут Data Flow Task. если Bulk Insert Task существенно производительней? Название статьи помните?
Мне кажется вы сами забыли название своей статьи, которое звучит как "Как быстрее всего передавать данные". Не вставлять, а передавать. А это и есть Dataflow. У которого под капотом может быть как bulk insert, так и bulk copy, который ещё быстрее чем bulk insert.
Во-первых, ни я, ни видимо Jovanny не заметили, чтобы вы хоть что-то сказали в статье про SSIS. Возможно, это было в форме стеганографии.
Во-вторых, посмотрите исходники /src/backend/commands/copyto.c. Где вы там увидели, что COPY — это в чём-то быстрее чем обычный Sequential scan SELECTа?
В-третьих, никаких текстовых файлов там и в помине нет. Вы, видимо, не понимаете разницы между SqlBulkCopy и BULK INSERT. В качестве примера набросал простой C# скрипт использующий аналогичный подход.
using var connection = new NpgsqlConnection("…pg connection…");
connection.Open();
using var command = new NpgsqlCommand("test_tbl", connection);
command.CommandType = CommandType.TableDirect;
using var bulk = new SqlBulkCopy("…mssql connection…", SqlBulkCopyOptions.TableLock);
bulk.EnableStreaming = true;
bulk.DestinationTableName = "test_tbl";
bulk.BatchSize = 1000000;
bulk.BulkCopyTimeout = 0;
var watch = new Stopwatch();
watch.Start();
{
using var reader = command.ExecuteReader();
bulk.WriteToServer(reader);
}
watch.Stop();
Console.WriteLine(watch.Elapsed);
Ни одного слова на SQL. И никаких "BULK INSERT" и этого самого SQL в профайлере. Чистый бинарный стриминг без промежуточных артефактов. Из физической таблицы в физическую перетекало где-то за 0.57+-0.02с. При этом, он работает гораздо более деликатно чем bcp.
В качестве референса, ваш EXEC с прилинкованной таблицей (тот который исполнялся CPU time = 8187 ms, elapsed time = 14793 ms) у меня даёт CPU time = 11343 ms, elapsed time = 16634 ms. То есть в целом сетап виртуалки медленнее, работает с IO на обоих концах, но делает это быстрее чем ваш вариант. При этом без всего этого колхоза с паролями и с понятным поведением, на тот случай если в таблице будет Unicode. Как вы собираетесь сращивать COPY TO, который не понимает utf-16 с bcp, который не понимает utf-8 мне пока не очень понятно. Возможно есть какой-то трюк, о котором я пока не знаю.
В-четвертых, у меня такое ощущение, что вы не понимаете разницы между SSIS и SSIS Server. SSIS — это небольшой тулкит, который идёт непосредственно с SQL Server, утилита dtexec для запуска трансформаций лежит в папочке DTS\Binn. Ведь даже простые экспорты/импорты SSMS, например, делает через SSIS. Что я, собственно, и сделал. Настроил импорт в SSMS из ODBC, сохранил в dtsx, и вызвал в консоли
PS:/> DTExec.exe /File copy.dtsx
DTExec: The package execution returned DTSER_SUCCESS (0).
Elapsed: 2.328 seconds
Для любопытства поднял размеры исходной таблицы в x10 и x100 раз.
Elapsed: 18.657 seconds
Elapsed: 179.016 seconds
И это без какого-либо тюнинга, то есть дефолтные 10K записей на батч, 3M буфер, не выключенные констрейнты, импорт в нормальную таблицу, а не временную (то есть база с Recovery Model full, а не simple как у tempdb), импорт из нормальной таблицы, а не SELECT'а, драйвер был выбран Unicode, маппинги толком не настроены. В общем где вы увидели доминирование bcp над SSIS — не понятно.
Я знаю, в статье это было оговорено. Ответ был на комментарий, который описывал решение "в принципе". А в принципе мне кажется, что используя нативные утилиты лучше начинать с нативных же форматов. Ваш же случай строится на множестве очень специфических деталей вроде наличия bcp под Linux, поддержки COPY TO PROGRAM и т.п. Вы же не использовали "специальную утилиту", как советует автор комментария.
Хотя и к вашей статье есть вопросы. Мне с трудом верится, что потенциально многопоточный бинарный SSIS, у которого под капотом есть и аналогичный bcp SqlBulkCopy, намного медленней однопоточного пайпа текстов в непонятной кодировке.
Один ваш комментарий ниже про "SSIS — выделенный хост" мне подсказываем, что вы даже не пробовали запустить dtexec CLI, который также как и bcp есть под линукс. Но я могу и ошибаться, поскольку не являюсь экспертом в этом области. Копирование больших объемов данных делаю не так часто, но с bcp, как и с dacpac/bacpac его использующим нахватался граблей, поскольку скорость не даётся бесплатно.
Техлиды потому и появились, что руководители для разработчиков лидерами зачастую не являются, не тот авторитет. Я бы так сказал, что техлид — это инженер с социальными перками/трейтами, говоря техническим языком. Техническим, потому как если взглянуть на HR глазами дизайнера (применяя хорошие практики вроде ISP, DIP и т.п.), то все эти проблемы распределения ответственностей обсуждаются не первое десятилетие и есть множество интересных решений.
У Александра богатый опыт переездов: жил и работал в Киргизии, Казахстане, России, Таиланде, Южной Корее.
С таким опытом Александр как никто другой знает ответ на первый же вопрос:
Чем крошка-страна лучше городов Европы?
Я так понимаю, любых городов. Даже если они будут различаться как Хельсинки и Эрисейра, например? Всегда думал, что одна из сильных сторон Европы — это разнообразие. Когда ты начинать день можешь серфингом на Фуэртавентуре, а заканчивать катаясь на сноуборде где-нибудь в Альпах, Рилах и т.п.
Я так думаю, что книга "Введение в профессию" рассчитана на тех, у кого профильное резюме девственно чистое. И конкурировать они будут с типовыми студентами. А помогая сегодняшним МГУшникам, которые пишут на C89 страшное спагетти, используя wgcc и FAR в качестве IDE, я бы однозначно выбрал тех, кто прочитал эту книгу. Хотя бы за то, что там в первом же томе идёт система типов, которая в Паскале одна из наиболее красивых. Такого человека гораздо проще внедрять в type-oriented разработку, пересаживать на диалекты ADT в современных Rust/TypeScript/Haskel|ML-семействах и т.п., да и просто, как правило, приятно работать с его кодом в большой команде. Даже если этот код потом будет на Си. А если есть хотя бы базовое понимание LISP/Scheme, то считай большая удача.
Добавил бы и про опытных подателей резюме. На дельфи огромное количество энтерпрайз-софта было написано. Когда веб-фреймворки только поднимались с колен, помимо него и, прости меня господи, 1С ничего более эффективного не было. И скрывать этот факт человеку, который годами автоматизировал круное производство, банки и т.п. только потому, что там фигурировал Pascal...
Мы для этой цели буквально за считанные дни написали симпатичные визуализаторы, с анимацией и куртизанками. И тесты писать не в пример легче. Декларативные conditions, с уклоном на type-driven разработку в итоговом счетё более маржинальны, чем спрятанные в коде императивные условия. И if-then, особенно с закрытыми third-party plugins, сильно усложняет систему. Как с точки зрения дизайна, так и последующей отладки.
SSG — это один из инструментов JAM. Мы же не называем колеса частным случаем машины. Если я для вашего примера возьму, скажем Hugo или Zola, то там не будет ни "J", ни "A". Странно называть такое решение JAM, не правда ли?)
То, что вы описываете — это SSG. JAMStack пребилдит в основном Markup и может делать пререндеринг страниц, но потом охотно использует API (который в свою очередь общается с базой или Headless CMS) и без труда рендерит на клиенте при помощи JavaScript (после регидрации обычно только так и рендерится). Собственно из подчеркнутых символов и формируется JAM. Кстати, вам до этого задали хороший вопрос, попробуйте добавить в свой пример с блогом комментарии пользователей.
Имхо, основная проблема исключений — не производительность, а отсутствие ссылочной прозрачности, говоря функциональным языком. Это такое ружьё, которые неизвестно где лежит и не понятно, когда выстрелит. Потому что нельзя просто так взять и использовать функцию выбрасывающую исключение где угодно.
И где это сказано? Некоторый DB-клиенты умеют видя localhost переходить на shared memory и другие IPC. COPY TO PROGRAM — это не просто память-память. Это пайп, файловая система, ACL и сериализация. Если TCP/IP — это порядка 80/1500 оверхеда, то сериализованные данные — это может быть в разы. Я не зря упоминал исходник, там не shared memory, там OpenPipeStream.
Я не говорю, что идея в корне плоха. Вполне может быть, что в Linux localhost TCP гораздо медленнее пайпов. Я знаю, что так было когда-то давно, но не следил, насколько что-либо изменилось. Для меня логичным выглядит взять и сравнить. Но с реальными конкурентами. А вы взяли самое медленное решение и на все конструктивные предложения сыпятся обвинения, что де вас не читают.
Я не только прочитал, а предоставил вам и код, и инструкции, как реализовать обмен именно между Postgres и MSSQL. В .NET бинарный обмен идёт через абстракцию DbDataReader. У SSIS таких абстракций из коробки побольше и маппинги получше. Естественно он не передаёт бинарный поток напрямую, но он не будет конвертировать interger из Postgres в string, добавлять к нему разделитель, что бы потом распарсить его обратно в int SQL Server'а.
Вы же понимаете, что процитированное вами я уже написал ранее:
Упоминая тот же sqlcmd, под IDE подразумевая SSMS, DataGrip и т.п., а "binary data stream" озвучив как "заливать данные прямо в TDS". Только я не могу понять, в чём загвоздка. Ваш bcp делает тоже самое. Только гоняет через текстовый формат, реконвертируя каждое значение
Зачем мне её перечитывать, если это ваша мания везде видеть BULK INSERT? Я его не использую. Заодно, чтобы укрепить свою уверенность, что bcp работает через BULK INSERT, попробуйте погонять его с опцией -n, когда данные идут в native-режиме, а не через csv/tsv-серилизацию. Я бы ещё предложил посмотреть профайлером и сниффером, но вряд ли у вас получится.
Я то как раз понимаю. Поэтому в примере и стоит TableDirect. Который в зависимости от провайдера может реализовываться по-разному. Поскольку передаёт данные протокол, а не SELECT. Я могу коннектится к No-SQL базе, я могу использовать LOB-протокол в постгресе, где не будет никаких SELECT вообще. Те же протоколы логической репликации или LISTEN/NOTIFY. Данные есть, а SELECT'ов нет. Так что у вас очень базовый, примитивный взгляд на предмет.
Я вообще не понимаю, зачем использовать древний rafms, когда tempfs=ramfs+swap+куча фич. Но вот когда заканчивается ram, то картина сильно меняется. Может, конечно, в вашем мире большие данные — это 366K записей по 12 байт, что еле-еле переваливает за 4М. В моей практике это хотя бы несколько терабайт на ноду. Но уж точно это не тот объём который помещается в RAM. Замените в своём generate_series 1K на 10K, 100K, 1000K и опубликуйте результаты, как я это сделал для dtexec. Посмотрим на динамику. Делов-то, несколько ноликов добавить.
Опять вы со своей песней. Где там в вашей статьей, что dtexec нельзя использовать как простую CLI как и bcp, дав ей те же реквизиты и tempdb, как и bcp, потому что… добавьте цитату из вашей статьи.
Да, мой косяк, забыл что с 2016 начал поддерживать.
Вы просто логически задумайтесь над своими же определениями. Утилита bcp у вас работает на постгрес-сервере, использует под капотом BULK INSERT. А какой сервер выполняет этот BULK INSERT? Определённо @@SERVERNAME, другого просто нет. Но BULK INSERT может читать только из файла. А где этот файл на @@SERVERNAME? И в итоге вся ваша схема не сходится.
Я бы предложил от боевых действий и попытки защитить свои позиции любой ценой перейти к конструктивным. Найти реально эффективное решение. Со своей стороны стараюсь вам подкидывать конкретные варианты для экспериментов, с вашей… осталось решить как этим воспользоваться))
Документацию прочтите. И маленький хинт для этого. T-SQL — это не весь MSSQL.
Вы так часто упрекаете других в невнимательном чтении, но сами не очень следуете своим стандартам. Я вам выше уже говорил, что включал профайлер. Там нет никаких BULK INSERT. И в коде SqlBulkCopy тоже нет никаких BULK INSERT.
Я понял, вы вообще не в курсе как оно работает. Так сфокусировались на BULK INSERT, что не удосужились узнать про INSERT BULK.
Да да, если посмотреть чуть внимательнее, есть и такая команда. А заглянув в спецификацию TDS, указание на которую вы проигнорировали, в п.2.2.6.1 узнаете, что есть такой Bulk Load BCP, начинающийся с команды "INSERT BULK", за которым идут метаданные и строки в виде raw data (таком же, как идут в протокол из SELECT).
Вы не можете вызвать эту команду из sqlcmd или какой-нибудь IDE, но она есть и активно используется в BCP, SSIS и т.п. Там нет никаких промежуточных файлов, можно заливать данные прямо в TDS без посредников. Вот пример как это используется в SqlBulkCopy, которую я задействовал в скрипте.
Сам SELECT ничего не передает. Можете код посмотреть, а можете поспекулировать с командами вроде SELECT INTO.
Конечно медленней (RDMA и мелланоксы не будем трогать). А вам рассказать, по какому протоколу bcp будет передавать данные на @@SERVERNAME из вашего примера? Только перед тем, как отправить всё по тому же TCP/IP вы решили ещё добавить сериализацию в текст (это когда timestamp, например, начинает занимать в 3 раза больше места) и десериализацию из него.
И в итоге создали узкое место — память. Вы целиком сериализуете всю таблицу и засовываете в ramfs, скармливаете батч размером 10M записей bcp, который также вынужен всё это буферизовать. Расскажите, плиз, что произойдёт когда таблица будет не мизерной как в вашем примере, а "относительно большой"?
Вообще то вы его взяли в качестве референса и сравнили с ним свою bcp-реализацию. Я взял его же в качестве референса и предложил альтернативный подход, дав данные для нормализации по референсному варианту. У вас сложности с проведением простой арифметической операции? Хотите я вам помогу?
Не неизвестно какую, а SELECT INTO из вашего generate_series, один-в-один. Это вы почему-то используете в задаче передачи данных суррогаты (с кардинально отличающимся EXPLAIN), я же делал приближённый к реальности вариант.
Мда… из всего сказанного вы заметили только числовое значение. Давайте проведём батл в AWS/Azure/etc по копированию терабайтной таблицы с текстом в Unicode. Когда ваш вариант уйдёт на дно в первые же секунды, а SSIS всё быстро перекачает.
С вашим вариантом я бы не сильно привередничал. Покажите его любому DBA, у него глаза будут кровоточить. Мой же "экспериментальный" хорошо документирован у MS (https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlbulkcopy), где проводятся и параллели с bcp.
Я очень внимательно читаю, просто вы пишете весьма странные вещи. Я вам говорю о простой консольной утилите dtexec, которую можно использовать вместо bcp. А вы мне про отсутствие поддержки Kerberos и даёте ссылку на статью, что де Hadoop с HDFS не поддерживаются. И одновременно сами сетуете:
То есть он у вас для "продуктивного использования подошёл", а к другой консольной утилите повышенные требования?
Что именно эта утилита не делает? Он быстро и эффективно передаёт таблицы любых размеров, с поддержкой Unicode, с поддержкой того же Kerberos. Там где ваше решение упадёт, это продолжить работать. Или суть именно в этом, что оно должно упасть?
Так это вы нам рассказываете, про секретные возможности bcp. Хотя те же самые пакеты 0x7 (Bulk Load), с которыми работает он, использует и SSIS. И они прекрасно документированы в спецификации протокола TDS. И с какой стати должна возникнуть разница, а также в какой момент bcp получил монополию, — пока не объяснили.
Мне кажется вы сами забыли название своей статьи, которое звучит как "Как быстрее всего передавать данные". Не вставлять, а передавать. А это и есть Dataflow. У которого под капотом может быть как bulk insert, так и bulk copy, который ещё быстрее чем bulk insert.
Во-первых, ни я, ни видимо Jovanny не заметили, чтобы вы хоть что-то сказали в статье про SSIS. Возможно, это было в форме стеганографии.
Во-вторых, посмотрите исходники /src/backend/commands/copyto.c. Где вы там увидели, что COPY — это в чём-то быстрее чем обычный Sequential scan SELECTа?
В-третьих, никаких текстовых файлов там и в помине нет. Вы, видимо, не понимаете разницы между SqlBulkCopy и BULK INSERT. В качестве примера набросал простой C# скрипт использующий аналогичный подход.
Ни одного слова на SQL. И никаких "BULK INSERT" и этого самого SQL в профайлере. Чистый бинарный стриминг без промежуточных артефактов. Из физической таблицы в физическую перетекало где-то за 0.57+-0.02с. При этом, он работает гораздо более деликатно чем bcp.
В качестве референса, ваш EXEC с прилинкованной таблицей (тот который исполнялся CPU time = 8187 ms, elapsed time = 14793 ms) у меня даёт CPU time = 11343 ms, elapsed time = 16634 ms. То есть в целом сетап виртуалки медленнее, работает с IO на обоих концах, но делает это быстрее чем ваш вариант. При этом без всего этого колхоза с паролями и с понятным поведением, на тот случай если в таблице будет Unicode. Как вы собираетесь сращивать COPY TO, который не понимает utf-16 с bcp, который не понимает utf-8 мне пока не очень понятно. Возможно есть какой-то трюк, о котором я пока не знаю.
В-четвертых, у меня такое ощущение, что вы не понимаете разницы между SSIS и SSIS Server. SSIS — это небольшой тулкит, который идёт непосредственно с SQL Server, утилита dtexec для запуска трансформаций лежит в папочке DTS\Binn. Ведь даже простые экспорты/импорты SSMS, например, делает через SSIS. Что я, собственно, и сделал. Настроил импорт в SSMS из ODBC, сохранил в dtsx, и вызвал в консоли
PS:/> DTExec.exe /File copy.dtsx
DTExec: The package execution returned DTSER_SUCCESS (0).
Elapsed: 2.328 seconds
Для любопытства поднял размеры исходной таблицы в x10 и x100 раз.
Elapsed: 18.657 seconds
Elapsed: 179.016 seconds
И это без какого-либо тюнинга, то есть дефолтные 10K записей на батч, 3M буфер, не выключенные констрейнты, импорт в нормальную таблицу, а не временную (то есть база с Recovery Model full, а не simple как у tempdb), импорт из нормальной таблицы, а не SELECT'а, драйвер был выбран Unicode, маппинги толком не настроены. В общем где вы увидели доминирование bcp над SSIS — не понятно.
В пятых, не нашёл никаких упоминаний на https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-ssis?view=sql-server-linux-2017, что это какой-то экспериментальный билд. Собственно говоря, dtexec там с тех же времен, что и bcp.
Я знаю, в статье это было оговорено. Ответ был на комментарий, который описывал решение "в принципе". А в принципе мне кажется, что используя нативные утилиты лучше начинать с нативных же форматов. Ваш же случай строится на множестве очень специфических деталей вроде наличия bcp под Linux, поддержки COPY TO PROGRAM и т.п. Вы же не использовали "специальную утилиту", как советует автор комментария.
Хотя и к вашей статье есть вопросы. Мне с трудом верится, что потенциально многопоточный бинарный SSIS, у которого под капотом есть и аналогичный bcp SqlBulkCopy, намного медленней однопоточного пайпа текстов в непонятной кодировке.
Один ваш комментарий ниже про "SSIS — выделенный хост" мне подсказываем, что вы даже не пробовали запустить dtexec CLI, который также как и bcp есть под линукс. Но я могу и ошибаться, поскольку не являюсь экспертом в этом области. Копирование больших объемов данных делаю не так часто, но с bcp, как и с dacpac/bacpac его использующим нахватался граблей, поскольку скорость не даётся бесплатно.
Рискну предположить, что для bcp самым быстрым является -n (native type).
Техлиды потому и появились, что руководители для разработчиков лидерами зачастую не являются, не тот авторитет. Я бы так сказал, что техлид — это инженер с социальными перками/трейтами, говоря техническим языком. Техническим, потому как если взглянуть на HR глазами дизайнера (применяя хорошие практики вроде ISP, DIP и т.п.), то все эти проблемы распределения ответственностей обсуждаются не первое десятилетие и есть множество интересных решений.
С таким опытом Александр как никто другой знает ответ на первый же вопрос:
Я так понимаю, любых городов. Даже если они будут различаться как Хельсинки и Эрисейра, например? Всегда думал, что одна из сильных сторон Европы — это разнообразие. Когда ты начинать день можешь серфингом на Фуэртавентуре, а заканчивать катаясь на сноуборде где-нибудь в Альпах, Рилах и т.п.
Я так думаю, что книга "Введение в профессию" рассчитана на тех, у кого профильное резюме девственно чистое. И конкурировать они будут с типовыми студентами. А помогая сегодняшним МГУшникам, которые пишут на C89 страшное спагетти, используя wgcc и FAR в качестве IDE, я бы однозначно выбрал тех, кто прочитал эту книгу. Хотя бы за то, что там в первом же томе идёт система типов, которая в Паскале одна из наиболее красивых. Такого человека гораздо проще внедрять в type-oriented разработку, пересаживать на диалекты ADT в современных Rust/TypeScript/Haskel|ML-семействах и т.п., да и просто, как правило, приятно работать с его кодом в большой команде. Даже если этот код потом будет на Си. А если есть хотя бы базовое понимание LISP/Scheme, то считай большая удача.
Добавил бы и про опытных подателей резюме. На дельфи огромное количество энтерпрайз-софта было написано. Когда веб-фреймворки только поднимались с колен, помимо него и, прости меня господи, 1С ничего более эффективного не было. И скрывать этот факт человеку, который годами автоматизировал круное производство, банки и т.п. только потому, что там фигурировал Pascal...
Мы для этой цели буквально за считанные дни написали симпатичные визуализаторы, с анимацией и куртизанками. И тесты писать не в пример легче. Декларативные conditions, с уклоном на type-driven разработку в итоговом счетё более маржинальны, чем спрятанные в коде императивные условия. И if-then, особенно с закрытыми third-party plugins, сильно усложняет систему. Как с точки зрения дизайна, так и последующей отладки.
SSG — это один из инструментов JAM. Мы же не называем колеса частным случаем машины. Если я для вашего примера возьму, скажем Hugo или Zola, то там не будет ни "J", ни "A". Странно называть такое решение JAM, не правда ли?)
То, что вы описываете — это SSG. JAMStack пребилдит в основном Markup и может делать пререндеринг страниц, но потом охотно использует API (который в свою очередь общается с базой или Headless CMS) и без труда рендерит на клиенте при помощи JavaScript (после регидрации обычно только так и рендерится). Собственно из подчеркнутых символов и формируется JAM. Кстати, вам до этого задали хороший вопрос, попробуйте добавить в свой пример с блогом комментарии пользователей.
Имхо, основная проблема исключений — не производительность, а отсутствие ссылочной прозрачности, говоря функциональным языком. Это такое ружьё, которые неизвестно где лежит и не понятно, когда выстрелит. Потому что нельзя просто так взять и использовать функцию выбрасывающую исключение где угодно.