2012-01-02 1 views
0

У меня есть база данных, содержащая 250 000 записей. Я использую DataReader для записи записей и экспорта в файл. Просто зацикливание записей с DataReader и без WHERE условий занимает около 22 минут. Я выбираю только два столбца (id и столбец nvarchar(max) с примерно 1000 символами в нем).Производительность SQL Server 2008 R2 Express DataReader

Звучит уже 22 минуты для SQL Server Express? Будет ли 1 ГБ ОЗУ или 1CPU повлиять на это?

+0

Больше тестирования - установлена ​​полная версия SQLServer 2008 R2 на том же компьютере с той же базой данных. DataReader запустил 250 000 записей за 4,3 минуты против 22 минут с помощью SQLExpress. – econner

+0

Вы говорите, что получаете доступ только к символам ~ 1k, но насколько велика фактическая таблица? Запустите 'exec sp_spaceused myTable' (замените' myTable' вашим именем таблицы). Максимальный размер для одной записи «NVARCHAR (MAX)» довольно велик, и поскольку вы не будете/не можете иметь индекс в поле «NVARCHAR», вы будете запрашивать всю строку, поэтому, если есть другой столбец скажем, 10 Кбайт в строке, ваши 250 тыс. строк на самом деле составляют 2,5 ГБ и т. д., что означает, что он не может полностью вписываться в ОЗУ. – Seph

+0

rows = 255,000. зарезервировано = 1994320 КБ, данные = 1911088 КБ, index_size = 82752 КБ, неиспользованный 480 КБ – econner

ответ

0

22 минуты звучат слишком долго для одного базового (неагрегационного) SELECT против 250K записей (даже 22 секунды звучат ужасно долго для меня).

Чтобы сказать, почему это могло бы помочь, если бы вы могли опубликовать код и определение своей схемы. У вас есть триггеры настроены?

С 1K символов в каждой записи (2KB) записи 250K (500MB) должны соответствовать пределу 1GB для SQL Express, поэтому память не должна быть проблемой только для этого запроса.

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

  • Contention из других приложений
  • Наличие строк, которые гораздо шире, чем просто две колонки вы упомянули
  • Чрезмерное дробление на диске таблицы или файла MDF DB
  • Медленное сетевое соединение между вашим приложением и БД

Обновление: я сделал быстрый тест. На моей машине чтение строк 250K 2KB с помощью SqlDataReader занимает менее 1 секунды.

Во-первых, создать тестовую таблицу с 256K строк (это всего около 30 секунд): веб-страницы

CREATE TABLE dbo.data (num int PRIMARY KEY, val nvarchar(max)) 
GO 
DECLARE @txt nvarchar(max) 
SET @txt = N'put 1000 characters here....' 
INSERT dbo.data VALUES (1, @txt); 
GO 
INSERT dbo.data 
    SELECT num + (SELECT COUNT(*) FROM dbo.data), val FROM dbo.data 
GO 18 

Тест для чтения данных и отображения статистики:

using System; 
using System.Collections; 
using System.Data.SqlClient; 
using System.Text; 

public partial class pages_default 
{ 
    protected override void OnLoad(EventArgs e) 
    { 
     base.OnLoad(e); 
     using (SqlConnection conn = new SqlConnection(DAL.ConnectionString)) 
     { 
      using (SqlCommand cmd = new SqlCommand("SELECT num, val FROM dbo.data", conn)) 
      { 
       conn.Open(); 
       conn.StatisticsEnabled = true; 
       using (SqlDataReader reader = cmd.ExecuteReader()) 
       { 
        while (reader.Read()) 
        { 
        } 
       } 
       StringBuilder result = new StringBuilder(); 
       IDictionary stats = conn.RetrieveStatistics(); 
       foreach (string key in stats.Keys) 
       { 
        result.Append(key); 
        result.Append(" = "); 
        result.Append(stats[key]); 
        result.Append("<br/>"); 
       } 
       this.info.Text = result.ToString(); 
      } 
     } 
    } 
} 

результаты (ExecutionTime в миллисекунды):

IduRows = 0 
Prepares = 0 
PreparedExecs = 0 
ConnectionTime = 930 
SelectCount = 1 
Transactions = 0 
BytesSent = 88 
NetworkServerTime = 0 
SumResultSets = 1 
BuffersReceived = 66324 
BytesReceived = 530586745 
UnpreparedExecs = 1 
ServerRoundtrips = 1 
IduCount = 0 
BuffersSent = 1 
ExecutionTime = 893 
SelectRows = 262144 
CursorOpens = 0 

Я повторил тест с SQL Enterprise и SQL Express, остроумие h аналогичные результаты.

Захват элемента «val» из каждой строки увеличился на ExecutionTime до 4093 мс (string val = (string)reader["val"];). Использование DataTable.Load(reader) заняло около 4600 мс.

Выполнение одного и того же запроса в SSMS заняло около 8 секунд, чтобы захватить все 256K строк.

+2

+1, но, пожалуйста, поместите 'SqlDataReader' в блок' using'. –

+0

Какие результаты вы получите, если прочитали из поля в цикле datareader? например, string test = reader ["val"]. ToString(); – econner

+0

Чтение поля «val» увеличивает время выполнения до 4093 мс. Выполнение запроса из SSMS, включая захват всех строк результатов, занимает около 8 секунд. – RickNZ

0

Ваши результаты запуска exec sp_spaceused myTable обеспечивают потенциальную подсказку:

rows = 255,000 
reserved = 1994320 KB 
data = 1911088 KB 
index_size = 82752 KB 
unused 480KB 

Важно отметить здесь reserved = 1994320 KB означает ваш стол некоторые +1866 Мб, при чтении полей, которые не индексируются (с NVARCHAR(MAX) не может быть индексированный) SQL Server должен прочитать всю строку в памяти, прежде чем ограничивать столбцы. Следовательно, вы легко бежите за пределом RAM 1 ГБ.

В качестве простого теста удалите последние (или первые) строки 150k и повторите запрос, чтобы узнать, какую производительность вы получите.

Несколько вопросов:

  • ли ваша таблица есть кластерный индекс по первичному ключу (это его id поле или что-то еще)?
  • Вы сортируете по столбцу, который не проиндексирован, например, поле `nvarchar(max)?

В лучшем случае для вас ваш ПК является id, а также кластерный индекс, и вы либо не имеют order by или вы order by id:

Предполагая, что ваш varchar(max) поле называется comments:

SELECT id, comments 
FROM myTable 
ORDER BY id 

Это будет работать нормально, но вам потребуется прочитать все строки в памяти (но это будет делать только один анализ за столом), так как comments - VARCHAR(MAX) и не может быть индексированный, а таблица - 2 ГБ, тогда SQL придется загружать таблицу в память по частям.

Скорее всего, что происходит, у вас есть что-то вроде этого:

SELECT id, comments 
FROM myTable 
ORDER BY comment_date 

Где comment_date это дополнительное поле, которое не проиндексирован. Поведение в этом случае будет заключаться в том, что SQL не сможет фактически отсортировать все строки в памяти, и в итоге ему придется несколько раз вывести таблицу из и из памяти, что может вызвать проблему, которую вы видите.

Простым решением в этом случае является добавление индекса в comment_date.

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

DECLARE @T TABLE 
(
id BIGINT, 
comments NVARCHAR(MAX), 
comment_date date 
) 

INSERT INTO @T SELECT id, comments, comment_date FROM myTable 

SELECT id, comments 
FROM @T 
ORDER BY comment_date 

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

Помимо всего этого выполните следующее после восстановления резервных копий для восстановления индексов и статистики, вы могли бы быть просто страдает от искаженных статистических данных (что происходит, когда вы резервное копирование фрагментарный базы данных, а затем восстановить его на новый экземпляр):

EXEC [sp_MSforeachtable] @command1="RAISERROR('UPDATE STATISTICS(''?'') ...',10,1) WITH NOWAIT UPDATE STATISTICS ? " 

EXEC [sp_MSforeachtable] @command1="RAISERROR('DBCC DBREINDEX(''?'') ...',10,1) WITH NOWAIT DBCC DBREINDEX('?')" 

EXEC [sp_MSforeachtable] @command1="RAISERROR('UPDATE STATISTICS(''?'') ...',10,1) WITH NOWAIT UPDATE STATISTICS ? " 
+0

Столбец id - тип Guid и установлен в Pk и кластеризуется для Pk. В это время я не устанавливаю условие WHERE или условие ORDER BY, а просто выполняю SELECT * и зацикливаю все 250 000 записей. Теперь я запускаю 3 команды, которые вы предоставили. Также я удалю 5000 записей из таблицы и посмотрю, не изменилось ли это значение, а затем опубликуйте результаты – econner