Тайм-аут запроса T-SQL / проблема производительности - PullRequest
1 голос
/ 08 декабря 2010

У меня есть таблица с около 1 миллиона записей.Структура таблицы показана ниже.Столбец UID - это первичный ключ и тип уникального идентификатора.

Table_A (содержит миллион записей)

UID                                            Name
-----------------------------------------------------------
E8CDD244-B8E4-4807-B04D-FE6FDB71F995           DummyRecord

У меня также есть функция с именем fn_Split('Guid_1,Guid_2,Guid_3,....,Guid_n'), которая принимает список разделенных запятыми направляющих и возвращает переменную таблицы, содержащую направляющие.

Из кода моего приложения я передаю запрос sql для получения новых указателей [Ключи, которые имеют код приложения, но отсутствуют в таблице базы данных]

var sb = new StringBuilder();
sb
.Append(" SELECT NewKey ")
.AppendFormat(" FROM fn_Split ('{0}') ", keyList)
.Append(" EXCEPT ")
.Append("SELECT UID from Table_A");

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

Спасибо.

Ответы [ 6 ]

2 голосов
/ 08 декабря 2010

Если вы используете MS SQL 2008, вы можете / должны использовать параметры TableValue. По сути, вы бы отправили свои направляющие в виде DataTable для вашей хранимой процедуры.

Тогда внутри вашей хранимой процедуры вы можете использовать параметры в качестве «таблицы» и выполнить объединение или ИСКЛЮЧИТЬ или что у вас есть, чтобы получить свои результаты.

Этот метод быстрее, чем использование функции для разделения, потому что функции на сервере MS SQL действительно медленные.

Но я полагаю, что время затрачивается из-за большого дискового ввода-вывода, который требуется для этого запроса. Поскольку вы выполняете поиск по столбцу UId и поскольку они «случайные», никакой индекс здесь не поможет. Двигателю придется прибегнуть к сканированию таблицы. Это означает, что вам понадобится серьезная производительность дискового ввода-вывода, чтобы получить результаты в «хорошее время».

Использование типа данных Uid в качестве индекса не рекомендуется. Тем не менее, это может не иметь значения в вашем случае. Но позвольте мне спросить вас об этом:

Руководства, которые вы отправляете из своего приложения, являются просто случайным списком руководств или здесь есть какие-то деловые отношения или отношения сущностей? Вполне возможно, что ваша модель данных не соответствует тому, что вы пытаетесь сделать. Так как же определить, по каким путеводителям вам нужно искать?

Однако, ради аргумента, давайте предположим, что ваши направляющие являются просто случайным выбором, тогда нет индекса, который действительно используется, так как ядро ​​базы данных должно будет выполнить сканирование таблицы, чтобы выбрать каждую из требуемых направляющих / записей из миллион записей у вас есть. В такой ситуации единственный способ ускорить процесс - это физический уровень базы данных, то есть физическое хранение ваших данных на жестких дисках и т. Д.

Например:

  1. Более быстрые диски улучшат производительность

  2. Если этот тип запроса запускается снова и снова, то поможет больше памяти на коробке, потому что движок может кэшировать данные в памяти, и ему не нужно будет выполнять физическое чтение

  3. Если вы разделите вашу таблицу, то движок может распараллелить операцию поиска и быстрее получить результаты.

  4. Если ваша таблица содержит много других полей, которые вам не всегда нужны, то разделение таблицы на две таблицы, где table1 содержит guid и минимальный минимальный набор полей, а table2 содержит остальные, ускорится. запрос совсем немного из-за требований дискового ввода-вывода меньше

  5. Здесь можно посмотреть множество других вещей

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

Наконец, в этом случае вы всегда можете увеличить свойство commandTimeOut, чтобы обойти проблемы тайм-аута.

Сколько времени требуется сейчас и какие улучшения вы ожидаете получить или надеетесь получить?

2 голосов
/ 08 декабря 2010

Здесь много информации о том, почему вы не должны использовать Guid для своего первичного ключа, особенно если он неупорядочен. Это было бы первым, что нужно исправить. Что касается вашего запроса, вы можете попробовать то, что предложили Пол или Тим, но, насколько я знаю, EXCEPT и NOT IN будут использовать один и тот же план выполнения, хотя в некоторых случаях OUTER JOIN может быть более эффективным.

2 голосов
/ 08 декабря 2010

Сначала добавьте индекс, если его нет, в table_a.uid, но я предполагаю, что он есть.

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

select newkey 
from fn_split
left outer join table_a
on newkey = uid
where uid IS NULL


select newkey 
from fn_split(blah)
where newkey not in (select uid 
                     from table_a)

select newkey 
from fn_split(blah) f
where not exists(select uid 
                 from table_a a 
                 where f.newkey = a.uid)
1 голос
/ 08 декабря 2010

Я спрашиваю, что вы делаете с этой информацией.

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

create procedure TryToInsert @GUID uniqueidentifier, @Name varchar(n) as
begin try
    insert into Table_A (UID,Name)
    values (@GUID, @Name);
    return 0;
end try
begin catch
    return 1;
end;

Во всех случаях вы можете разделить KeyList на клиенте, чтобы получить более быстрые результаты - и вы можете запросить недопустимые ключи:

select  UID
from    Table_A
where   UID in ('new guid','new guid',...);

Если GUID случайный, вы должны использовать newsequentialid () с кластеризованным первичным ключом:

create table Table_A (
    UID uniqueidentifier default newsequentialid() primary key,
    Name varchar(n) not null
);

С этим вы можете вставить и запросить вновь вставленные данные за один шаг:

insert into Table_A (Name)
output inserted.*
values (@Name);

... только мои два цента

1 голос
/ 08 декабря 2010

Если я правильно понимаю ваш вопрос, в вашем клиентском коде у вас есть разделенная запятыми строка (строковых) идентификаторов GUID.Эти GUIDS могут использоваться клиентом, только если они еще не существуют в TableA.Не могли бы вы вызвать SP, который создает временную таблицу на сервере, содержащую потенциально используемые GUIDS, и затем сделать это:

        select guid from #myTempTable as temp
        where not exists
           (
            select uid from TABLEA where uid = temp.guid
            )

Вы можете передать свою строку GUIDS в SP;он заполнил бы временную таблицу, используя вашу функцию;и затем верните ADO.NET DataTable клиенту.Это должно быть очень легко проверить, прежде чем вы даже потрудитесь написать SP.

0 голосов
/ 08 декабря 2010

В любом случае, не являются ли GUID по своей сути уникальными для всех целей и задач? (т. е. универсально уникально - не имеет значения, где генерируется). Я бы даже не потрудился сделать тест заранее; просто вставьте строку с GUID PK и, если вставка не удалась, отмените GUID. Но это не должно провалиться, если только это не действительно GUID.

http://en.wikipedia.org/wiki/GUID

http://msdn.microsoft.com/en-us/library/ms190215.aspx

Кажется, вы выполняете много ненужной работы, но, возможно, я не понимаю требования вашего приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...