Запуск программ ObjectScript в межсистемном кэше для предоставления данных с помощью SQL Server.

Задний план

Основное приложение, в котором я работаю, в значительной степени основано на движке базы данных Caché в стиле MUMPS от InterSystems. Все хранится в глобальных массивах. Получение данных из системы для внешней отчетности варьируется от простой боли до вопиюще медленной и болезненной.

Caché предоставляет ODBC-драйвер для базы данных, но если глобальные массивы не имеют ключей в соответствии с критериями выбора, он прибегает к сканированию, и выполнение простого запроса займет несколько часов. Для масштабирования все рабочее пространство имен Caché составляет около 100 ГБ. Я могу писать программы на ObjectScript (диалект MUMPS от Intersystems), которые в этих случаях извлекают данные намного быстрее, чем драйвер ODBC.

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

Я создал базу данных отчетов в MS SQL Server, которая извлекает наиболее распространенные данные (объемом 2,5 ГБ), и даже если ей нужно сканировать каждую таблицу, все результаты возвращаются в течение 3 секунд. К сожалению, обновление данных занимает много времени, поэтому я могу выполнять полное обновление только один раз в неделю и активное обновление один раз в день. Этого достаточно для большинства потребностей, но я хочу сделать лучше.

Я использую Caché 2007, SQL Server 2008 R2, VS2010 в Windows 7 и Windows Server 2008 R2.

Объем вопроса

Мне нужен способ интеграции оперативных данных из исходной базы данных Caché с другими данными на SQL Server. Я хочу иметь возможность интегрировать представления или табличные функции в SQL-запрос и получать данные в реальном времени из исходной базы данных.

  • Оперативные данные должны быть доступны в SQL Server для обработки. Выполнение этого с дополнительным приложением было бы огромной проблемой и не работало бы с инструментами отчетности, которые просто ожидают отправки запроса через ODBC и получения окончательного набора данных в правильном формате.

  • Я понимаю, что есть способы получить данные в SQL Server или выполнить те же общие действия, которые я хочу сделать. Этот вопрос не об этом.

  • Данные должны поступать из программ ObjectScript, запущенных в Caché, поскольку не все данные, которые мне нужны, отображаются через таблицы, определенные SQL, и я получаю контроль, необходимый для обеспечения производительности, пригодной для использования с ObjectScript.

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

Что я пробовал до сих пор

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

  1. Вытягивание всего через драйвер InterSystem Caché ODBC через связанный сервер. SQL Server часто прибегает к сканированию, если он не может отправить условия на удаленный сервер или должен выполнить соединение локально. Сканирование любой нетривиальной таблицы занимает много часов и недопустимо. Кроме того, длина многих столбцов неправильно определяется определениями таблиц SQL в Caché; SQL Server это не нравится и прерывает запрос. См. этот вопрос SO . Я не могу изменить определение таблицы, и поставщик не считает это проблемой, поскольку она работает с MS Access.

  2. Использование OPENQUERY по запросу. Это работает до некоторой степени, но у меня все еще может быть проблема с длиной столбца из предыдущего пункта, и нет способа параметризовать запросы OPENQUERY, поэтому извлекать контекстные данные довольно бесполезно.

  3. Использование SQLCLR для вызова поставщика данных ODBC с помощью табличных функций CLR. Это решает проблемы параметризации и длины данных, хотя и требует от меня определения или изменения функции каждый раз, когда мне нужен новый фрагмент данных. К сожалению, не все интересующие меня элементы данных доступны через SQL. Для некоторых вещей мне нужен прямой доступ к глобальным массивам.

  4. Intersystems предоставляет элемент управления ActiveX, который позволяет запускать программы ObjectScript через TCP на сервере и получать результаты. Это прекрасно работает в автономном приложении C#, но как только я пытаюсь установить соединение из сборки SQLCLR, я получаю нелепую ошибку URI:

    Произошла ошибка .NET Framework во время выполнения определяемой пользователем подпрограммы или агрегата «GetActiveAccounts»: System.UriFormatException: Invalid URI: URI пуст. System.UriFormatException: в System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind) в System.Uri..ctor(String uriString) в System.ComponentModel.Design.RuntimeLicenseContext.GetLocalPath(String fileName) в System.ComponentModel. Design.RuntimeLicenseContext.GetSavedLicenseKey(Type type, Assembly resourceAssembly) at System.ComponentModel.LicenseManager.LicenseInteropHelper.GetCurrentContextInfo(Int32& fDesignTime, IntPtr& bstrKey, RuntimeTypeHandle rth) at FacsAccess.GetActiveAccounts.Client.connect() at FacsAccess.GetActiveAccounts ctor() в FacsAccess.GetActiveAccounts.E1.GetEnumerator()

    См. этот SO-вопрос без ответа. Есть и другие сообщения. об этом в сети, но никто, кажется, не имеет ни малейшего представления. Это чрезвычайно простая оболочка COM над C++ DLL; он ничего не делает с лицензированием и не имеет причин быть в управляемых библиотеках лицензирования. Интересно, это какой-то шаблон, который пытается получить имя для сборки, у которой нет имени, потому что она была загружена в базу данных SQL.

  5. Intersystems также предоставляет более прямой неуправляемый интерфейс но все эти интерфейсы — C++, которые я не могу использовать через P/Invoke, и я не могу загрузить нечистую сборку смешанного режима C++/CLI в SQLCLR.

Варианты, которые я рассматривал, но кажутся ужасными

  1. Я думал попробовать управление ActiveX через COM-поддержку SQL Server, но это ужасно медленно и очень громоздко.

  2. Я мог бы создать внепроцессную службу для прокси-трафика, но я не могу использовать удаленное взаимодействие .NET из SQLCLR, и вы не должны использовать WCF, и в любом случае это будет очень тяжело для такого простого интерфейса. Я бы скорее свой собственный интерфейс IPC закатал.

  3. Я мог бы написать какую-нибудь дополнительную неуправляемую оболочку с интерфейсом в стиле C для интерфейсов VisM или CacheDirect и получить доступ к ЭТОМУ через P/Invoke.

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


person Chris Smith    schedule 20.06.2012    source источник


Ответы (1)


Я думаю, вы можете использовать ODBC через связанный сервер, обращающийся к хранимым процедурам в базе данных Cache, которые видны драйверу ODBC и которые возвращают наборы результатов, но не реализованы с использованием SQL.

Я на 100% уверен, что вы можете создавать такие хранимые процедуры и получать к ним доступ через ODBC, но я никогда не пытался получить к ним доступ с SQL-сервера в качестве связанного сервера. Даже если связанный сервер не работает, кажется, что было бы предпочтительнее получить доступ через драйвер ODBC Intersystem, а не через элемент управления Active X или CacheDirect.

У меня есть пример такой процедуры для этот вопрос.

В случае, если эта ссылка умирает, вот код:

Query OneGlobal(GlobalName As %String) As %Query(ROWSPEC = "NodeValue:%String,Sub1:%String,Sub2:%String,Sub3:%String,Sub4:%String,Sub5:%String,Sub6:%String,Sub7:%String,Sub8:%String,Sub9:%String") [SqlProc]
{
}

ClassMethod OneGlobalExecute(ByRef qHandle As %Binary, GlobalName As %String) As %Status
{
    S qHandle="^"_GlobalName
    Quit $$$OK
}

ClassMethod OneGlobalClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = OneGlobalExecute ]
{
    Quit $$$OK
}

ClassMethod OneGlobalFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = OneGlobalExecute ]
{

    S Q=qHandle  
    S Q=$Q(@Q)  b  
    I Q="" S Row="",AtEnd=1 Q $$$OK
    S Depth=$QL(Q)
    S $LI(Row,1)=$G(@Q)
    F I=1:1:Depth S $LI(Row,I+1)=$QS(Q,I)
    F I=Depth+1:1:9 S $LI(Row,I+1)=""
    S AtEnd=0
    S qHandle=Q
    Quit $$$OK
}

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

Тем не менее, похоже, что вы были готовы написать Cache Object Script для прямого получения данных в любом случае - это шаблон для этого. Главное понимать, что qHandle будет передаваться обратно драйвером ODBC при каждом вызове, поэтому вы можете использовать его для хранения состояния. Если состояний много, сделайте qHandle целочисленным индексом во временном глобальном, содержащем «реальное» состояние, и очистите его в методе close.

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

MyQueryFetchRows (ByRef qHandle As %Binary, FetchCount As %Integer = 0, ByRef RowSet As %List, ByRef ReturnCount As %Integer, ByRef AtEnd As %Integer) As %Status

метод — см. документацию для %Library.Query для более подробной информации.

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

person psr    schedule 20.06.2012
comment
Интересный. Я обязательно посмотрю на это. Спасибо! - person Chris Smith; 20.06.2012
comment
Ни один из индексов в глобальных переменных, на которые я смотрел, не имеет имен, поэтому $QSUBSCRIPT всегда возвращает . Выделяет ли это процесс Caché для всего запроса ODBC, и если да, то могу ли я просто использовать частные глобальные переменные процесса для состояния? Нужно ли мне беспокоиться о реализации GetODBCInfo? - person Chris Smith; 20.06.2012
comment
Нижний индекс не может быть пустой строкой (хотя $QSUB может возвращать пустую строку), поэтому я не уверен, что вы имеете в виду - возможно, это число? Однако код должен работать. В любом случае, почему бы не реализовать цикл for с помощью этого метода — вам не придется отлаживать немного странные вещи, такие как $QSUB. И нет, вы не можете с уверенностью предполагать сходство процессов. Вам не нужно реализовывать GetODBCInfo. - person psr; 20.06.2012
comment
Я имею в виду, что для второго параметра $QS я получаю для каждого ввода, кроме 0, который повторяет мне глобальное имя. Хотя это не очень важно. - person Chris Smith; 21.06.2012
comment
Я решил использовать внепроцессный прокси-сервис, используя общую память для связи. Таким образом, я могу хранить в процессе столько состояний, сколько захочу, и мне не нужно втискиваться в qHandle. Вы были единственным, кто ответил на мой вопрос, и этот метод действительно работает, поэтому я принимаю ваш ответ. Спасибо. - person Chris Smith; 25.06.2012
comment
@ChrisSmith - Может быть, вы можете опубликовать код в качестве ответа, как только вы его реализовали. - person psr; 25.06.2012
comment
@psr На данный момент я использую VisM в качестве элемента управления ActiveX для vb.net. Как получить список GLOBALS (даже если он не отформатирован) с определенным именем (пример: ^BACKTR), используя этот активный элемент управления X? - person Malcolm Salvador; 27.03.2016
comment
@Malky.Kid - Похоже, вам следует опубликовать отдельный вопрос. Обратите внимание, что из вашего комментария мне неясно, что означает получение списка GLOBALS. Вы имеете в виду все пары ключ-значение в конкретном глобальном? Включая глобальные узлы с несколькими ключами? Или вам нужен список глобальных имен (я так не думаю, но не уверен)? Возвращаемое значение для VisM в основном представляет собой строку — что вы хотите сделать, если результаты слишком велики для строки? Как вы хотите отформатировать строку? Неясные вопросы обычно закрывают, поэтому подумайте, как объяснить, что именно вы пытаетесь сделать. - person psr; 28.03.2016