Могу ли я распределить длительный хранимый процесс по нескольким процессорам? - PullRequest
4 голосов
/ 05 марта 2010

[Также в SuperUser - https://superuser.com/questions/116600/can-i-spead-out-a-long-running-stored-proc-accross-multiple-cpus]

У меня есть хранимая процедура в SQL-сервере, которая получает и расшифровывает блок данных.(Кредитные карты в данном случае.)

В большинстве случаев производительность терпима, но есть пара клиентов, у которых процесс мучительно медленный и занимает буквально 1 минуту.(Ну, если быть точным, 59377 мс для возврата из SQL Server, но он может варьироваться на несколько сотен мс в зависимости от нагрузки)

Когда я наблюдаю за процессом, я вижу, что для выполнения SQL используется только один процессвесь процесс, и, как правило, только proc 0.

Есть ли способ, которым я могу изменить свой хранимый процесс, чтобы SQL мог многопоточность процесса?Возможно ли даже обмануть и разбить вызовы пополам (верхние 50%, нижние 50%) и распределить нагрузку, как грубый взлом?(просто плевок здесь)

Мой сохраненный процесс:

USE [Commerce]
GO
/****** Object:  StoredProcedure [dbo].[GetAllCreditCardsByCustomerId]    Script Date: 03/05/2010 11:50:14 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[GetAllCreditCardsByCustomerId]
@companyId UNIQUEIDENTIFIER, @DecryptionKey NVARCHAR (MAX)
AS
SET NoCount ON

DECLARE @cardId uniqueidentifier
DECLARE @tmpdecryptedCardData VarChar(MAX);
DECLARE @decryptedCardData VarChar(MAX);

    DECLARE @tmpTable as Table 
    (
        CardId uniqueidentifier,
        DecryptedCard NVarChar(Max)
    )

DECLARE creditCards CURSOR FAST_FORWARD READ_ONLY
  FOR  Select cardId from CreditCards where companyId = @companyId and Active=1 order by addedBy desc



--2 
OPEN creditCards
--3 
FETCH creditCards INTO @cardId   -- prime the cursor

WHILE @@Fetch_Status = 0 
  BEGIN

        --OPEN creditCards
        DECLARE creditCardData CURSOR FAST_FORWARD READ_ONLY
                        FOR select convert(nvarchar(max), DecryptByCert(Cert_Id('Oh-Nay-Nay'), EncryptedCard, @DecryptionKey)) FROM CreditCardData where cardid = @cardId order by valueOrder

                OPEN creditCardData

                FETCH creditCardData INTO @tmpdecryptedCardData   -- prime the cursor

                WHILE @@Fetch_Status = 0 
                    BEGIN               

                        print 'CreditCardData'
                        print @tmpdecryptedCardData                     

                        set @decryptedCardData = ISNULL(@decryptedCardData, '') + @tmpdecryptedCardData
                        print '@decryptedCardData'
                        print @decryptedCardData;

                        FETCH NEXT FROM creditCardData INTO @tmpdecryptedCardData   -- fetch next
                    END 
                    CLOSE creditCardData
                    DEALLOCATE creditCardData       

                    insert into @tmpTable (CardId, DecryptedCard) values (  @cardId, @decryptedCardData )
                    set @decryptedCardData = ''


    FETCH NEXT FROM creditCards INTO @cardId   -- fetch next
  END

select CardId, DecryptedCard FROM @tmpTable


CLOSE creditCards
DEALLOCATE creditCards

Ответы [ 4 ]

1 голос
/ 08 марта 2010

Как насчет использования FOR XML для конкатенации в одном коррелированном подзапросе:

DECLARE @cards TABLE
    (
     cardid INT NOT NULL
    ,addedBy INT NOT NULL
    )
DECLARE @data TABLE
    (
     cardid INT NOT NULL
    ,valueOrder INT NOT NULL
    ,encrypted VARCHAR(MAX) NOT NULL
    )

INSERT  INTO @cards
VALUES  ( 0, 1 )
INSERT  INTO @cards
VALUES  ( 1, 0 )

INSERT  INTO @data
VALUES  ( 0, 0, '0encrypted0' )
INSERT  INTO @data
VALUES  ( 0, 1, '0encrypted1' )
INSERT  INTO @data
VALUES  ( 0, 2, '0encrypted2' )
INSERT  INTO @data
VALUES  ( 1, 0, '1encrypted0' )
INSERT  INTO @data
VALUES  ( 1, 1, '1encrypted1' )

-- INSERT INTO output_table ()
SELECT  cardid, decrypted
FROM    @cards AS cards
        OUTER APPLY ( SELECT    REPLACE(encrypted, 'encrypted', 'decrypted') + '' -- Put your UDF here
                      FROM      @data AS data
                      WHERE     data.cardid = cards.cardid
                      ORDER BY  data.valueOrder
                    FOR
                      XML PATH('')
                    ) AS data ( decrypted )
ORDER BY cards.addedBy DESC
0 голосов
/ 09 марта 2010

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

0 голосов
/ 08 марта 2010

Считайте, что номера кредитных карт очень хорошо хэшируются - последняя цифра в 16-значных CC Visa / MasterCard является контрольной суммой. Рассматривали ли вы параллельность по принципу «катите сами», например, чтобы каждый поток брал те номера CC, где modulo (4) = thread_id? Предполагая, что n процессоров / ядер / как бы они их ни называли сегодня, вам не нужно больше 4 (2 * ядер) потоков параллельной обработки.

0 голосов
/ 05 марта 2010

Это может быть лучшим вопросом для группы SuperUser (DBA)

...