Производительность SQL Server 2008 R2 Express DataReader - PullRequest
0 голосов
/ 02 января 2012

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

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

Ответы [ 2 ]

0 голосов
/ 05 января 2012

Ваши результаты выполнения 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 должен прочитать всю строку в память, прежде чем ограничивать колонны. Следовательно, вы легко бежите за пределы 1 ГБ ОЗУ.

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

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

  • Имеет ли ваша таблица кластеризованный индекс первичного ключа (это поле 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 голосов
/ 02 января 2012

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

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

Если в каждой записи (2 КБ) по 1 КБ, записи по 250 КБ (500 МБ) должны умещаться в пределах 1 ГБ SQL Express, поэтому память не должна быть проблемой только для этого запроса.

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

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

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

Сначала создайте тестовую таблицу с 256КБ строк (это заняло всего около 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();
            }
        }
    }
}

Результаты (время выполнения в миллисекундах):

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, с похожими результатами.

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

Выполнение одного и того же запроса в SSMS заняло около 8 секунд для захвата всех строк 256 КБ.

...