Невыполнимая задача?Умные взгляды на защищенные базы данных на нескольких серверах - PullRequest
0 голосов
/ 12 июня 2019

Наш ИТ-отдел унаследовал абсолютный кошмар системы баз данных. У нас есть 5 серверов баз данных (3 из которых MSSQL 2008R2 и 2 - MSSQL 2016). Вместо того, чтобы быть многопользовательскими, они однопользовательские. Это означает, что у нас есть сотни шардов на 5 серверах (у нас есть более 300 шардов базы данных).

Для запроса ВСЕХ данных по всем сегментам у каждого сервера есть «родительская» база данных, состоящая из представлений, которые затем СОЕДИНЯЮТ ВСЕ данные из каждого сегмента на сервере в одно представление (мы называем их «Представления между серверами»). ). Затем на «материнском» сервере межсерверное представление использует связанные серверные соединения с 4 «дочерними» серверами для извлечения их данных.

По сути, это выглядит так: enter image description here

Если мы хотим получить ВСЕ данные от клиентов, мы выполняем простой запрос SELECT * FROM CUSTOMERS для сервера базы данных «main» / «mother», где CUSTOMERS - это представление, которое получает данные из своих собственных сегментов, плюс другие 4 "дочерних" сервера. Каждый «дочерний» сервер БД также имеет представление «Клиенты», которое получает данные из своих собственных сегментов. Это «материнское» представление построено так:

--Its own shards, plus the rollup views from the child db servers
select * from shard1..Customers union all 
select * from shard2..Customers union all 
select * from shard3..Customers union all 
--etc... union all
select * from DB2..Customers union all 
select * from DB3..Customers union all 
select * from DB4..Customers union all 
select * from DB5..Customers

и каждое из «дочерних» представлений строится так:

--ONLY its own shards
select * from shard4..Customers union all
select * from shard5..Customers union all
--etc...  

Всего существует более 300 осколков, поэтому эти представления намного больше, чем я описал выше. В основном, большой волосатый беспорядок !!!!

Из-за этой архитектуры у нас есть проблема в том, что если один осколок выйдет из строя, все они выйдут из строя (схемы ВСЕГДА будут идентичны, поэтому мы пропускаем эту проблему). Перед нами была поставлена ​​задача переписать эти представления, чтобы они были «умными» в том смысле, что если шард или дочерний сервер недоступен, он пропустит их и вернет данные для найденных шардов / серверов.

Я думал, что у меня есть решение, которое работало нормально, пока я не добрался до двух последних серверов, которые являются серверами MSSQL 2016.

В двух словах, я создал хранимую процедуру, которая собирала имена баз данных через sys.databases, исключил базы данных, которые нам не нужны, и сгенерировал динамический SQL-оператор COALESCE'd, который затем выполнялся через sp_executesql (код ниже ):

    SET FMTONLY OFF;
    SET NOCOUNT ON;

    DECLARE @sql NVARCHAR(MAX)

    SELECT @sql = COALESCE(@sql + ' UNION ALL ', '') + 'SELECT * FROM ' + name + '.dbo.' + @table + ' WITH (NOLOCK)'
    FROM sys.databases
    WHERE state_desc = 'ONLINE'
    AND name NOT IN ('master', 'model', 'msdb', 'tempdb', 'DBLOOKUP', 'MM_STATE_MT', 'kids', 'distribution', 'LiteSpeedLocal')

    IF (SELECT @@SERVERNAME) = 'MOTHER'
    BEGIN
        DECLARE @available INT

        BEGIN TRY
            EXEC @available = sys.sp_testlinkedserver N'CHILD1'
            SET @sql = @sql + ' UNION ALL SELECT * FROM [CHILD1].viewdb.dbo.' + @table + ' WITH (NOLOCK)'
        END TRY
        BEGIN CATCH
            SET @available = 0
        END CATCH

        BEGIN TRY
            EXEC @available = sys.sp_testlinkedserver N'CHILD2'
            SET @sql = @sql + ' UNION ALL SELECT * FROM [CHILD2].viewdb.dbo.' + @table + ' WITH (NOLOCK)'
        END TRY
        BEGIN CATCH
            SET @available = 0
        END CATCH

        BEGIN TRY
            EXEC @available = sys.sp_testlinkedserver N'CHILD3'
            SET @sql = @sql + ' UNION ALL SELECT * FROM [CHILD3].viewdb.dbo.' + @table + ' WITH (NOLOCK)'
        END TRY
        BEGIN CATCH
            SET @available = 0
        END CATCH

        BEGIN TRY
            EXEC @available = sys.sp_testlinkedserver N'CHILD4'
            SET @sql = @sql + ' UNION ALL SELECT * FROM [CHILD4].viewdb.dbo.' + @table + ' WITH (NOLOCK)'
        END TRY
        BEGIN CATCH
            SET @available = 0
        END CATCH
    END

    EXEC sp_executesql @sql;

Затем в представлениях viewdb я использовал связанный сервер с самоссылкой и OPENQUERY для выполнения процедуры следующим образом (удалил имя хранимой процедуры и имя таблицы):

SELECT * FROM OPENQUERY(local, 'SET FMTONLY OFF; SET NOCOUNT ON; EXEC [stored procedure] ''[table_we_want_to_query]''')

Это отлично работало на серверах MSSQL 2008 из-за того, что FMTONLY OFF работает в этой редакции. Однако, начиная с MSSQL 2012, они изменили работу FMTONLY. Поэтому на серверах 2016 года, если мы попытаемся запросить представления, мы получим следующую ошибку:

The metadata could not be determined because statement 'EXEC sp_executesql @sql' in procedure '[stored procedure]'  contains dynamic SQL.  Consider using the WITH RESULT SETS clause to explicitly describe the result set.

Я пробовал С РЕЗУЛЬТАТАМИ РЕЗУЛЬТАТОВ, который не работает. Я не могу указать результирующий набор в хранимой процедуре, потому что он является общим (принимает параметр ввода имени таблицы, чтобы определить, какую таблицу запрашивать).

Я испробовал все способы динамического sql и переключения параметров, которые я могу придумать или найти через Google. Я мертв в воде с этим процессом на серверах 2016 года. У кого-нибудь есть идеи, как обойти этот или другой подход к решению проблемы? Имейте в виду, что изменение всей архитектуры базы данных в настоящее время недоступно. Мы должны работать с тем, что имеем сейчас.

Пожалуйста, дайте мне знать, если есть какая-либо дополнительная информация, которая может быть полезной. Спасибо!

...