Медленно выберите производительность на столе - PullRequest
0 голосов
/ 09 мая 2018

Я использую SQL Server, и у меня есть такая таблица

CREATE TABLE dbo.CompanyRolesExpanded (
  StaticId uniqueidentifier NOT NULL,
  UserId uniqueidentifier NULL,
  UserGroupId uniqueidentifier NULL,
  CompanyId uniqueidentifier NULL,
  CompanyGroupId uniqueidentifier NULL,
  CompanyAccessUnitRole uniqueidentifier NULL,
  PRIMARY KEY CLUSTERED (StaticId)
)
GO

На данный момент в этой таблице около 3 миллионов строк. Простой выбор, как этот, занимает около 30 секунд

SELECT  UserId,UserGroupId
       ,CompanyId,CompanyGroupId 
       ,CompanyAccessUnitRole                   
FROM CompanyRolesExpanded

Есть ли способ улучшить это?

Ответы [ 2 ]

0 голосов
/ 09 мая 2018

Я не думаю, что проводники - это длинный столб в палатке с точки зрения производительности в этом случае. Выполнение теста PowerShell, показанного ниже, для выбора строки 3M с удаленного сервера, результаты показали, что тест int в среднем примерно на 10% быстрее. Если предположить, что аналогичные результаты в вашей среде, это будет означать, что 27 секунд с int против 30 с guid. Я заметил, что большую часть времени было связано с обработкой клиентским ЦП большого набора результатов.

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

$connectionString = "Data Source=YourServer;Initial Catalog=tempdb;Integrated Security=SSPI;Application Name=PerformanceTestScript";

$guidSetupScript = @"
CREATE TABLE dbo.Example (
  StaticId uniqueidentifier NOT NULL,
  UserId uniqueidentifier NULL,
  UserGroupId uniqueidentifier NULL,
  CompanyId uniqueidentifier NULL,
  CompanyGroupId uniqueidentifier NULL,
  CompanyAccessUnitRole uniqueidentifier NULL,
  PRIMARY KEY CLUSTERED (StaticId)
);
WITH 
     t10 AS (SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(n))
    ,t1k AS (SELECT 0 AS n FROM t10 AS a CROSS JOIN t10 AS b CROSS JOIN t10 AS c)
    ,t10m AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS num FROM t1k AS a CROSS JOIN t1k AS b CROSS JOIN t1k AS c CROSS JOIN t10 AS d)
INSERT INTO dbo.Example WITH(TABLOCKX) (
      StaticId
    , UserId
    , UserGroupId
    , CompanyId
    , CompanyGroupId
    , CompanyAccessUnitRole
    )
SELECT
      NEWID()
    , NEWID()
    , NEWID()
    , NEWID()
    , NEWID()
    , NEWID()
FROM t10m
WHERE num <= 3000000;
"@

$intSetupScript = @"
CREATE TABLE dbo.Example (
  StaticId int NOT NULL,
  UserId int NULL,
  UserGroupId int NULL,
  CompanyId int NULL,
  CompanyGroupId int NULL,
  CompanyAccessUnitRole int NULL,
  PRIMARY KEY CLUSTERED (StaticId)
);
WITH 
     t10 AS (SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(n))
    ,t1k AS (SELECT 0 AS n FROM t10 AS a CROSS JOIN t10 AS b CROSS JOIN t10 AS c)
    ,t10m AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS num FROM t1k AS a CROSS JOIN t1k AS b CROSS JOIN t1k AS c CROSS JOIN t10 AS d)
INSERT INTO dbo.Example WITH(TABLOCKX) (
      StaticId
    , UserId
    , UserGroupId
    , CompanyId
    , CompanyGroupId
    , CompanyAccessUnitRole
    )
SELECT
      num
    , num
    , num
    , num
    , num
    , num
FROM t10m
WHERE num <= 3000000;
"@

try
{
    $values = [System.Array]::CreateInstance([System.Object], 6)
    $connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
    $command = New-Object System.Data.SqlClient.SqlCommand
    $command.CommandTimeout = 0
    $connection.Open()
    $command.Connection = $connection

    #Guid setup
    $command.CommandText = "IF OBJECT_ID(N'dbo.Example') IS NOT NULL DROP TABLE dbo.Example;"
    [void]$command.ExecuteNonQuery()
    $command.CommandText = $guidSetupScript
    [void]$command.ExecuteNonQuery()

    #guid test
    $testSw = [System.Diagnostics.StopWatch]::StartNew()
    Write-Host "Starting Guid test."
    $command.CommandText = "SELECT * FROM dbo.Example;"
    $reader = $command.ExecuteReader()
    while($reader.Read()) {
        $values = $reader.GetValues($values)
    }
    $reader.Close()
    $testSw.Stop()
    Write-Host "Guid test duration was $($testSw.Elapsed.ToString())"

    #int setup
    $command.CommandText = "IF OBJECT_ID(N'dbo.Example') IS NOT NULL DROP TABLE dbo.Example;"
    [void]$command.ExecuteNonQuery()
    $command.CommandText = $intSetupScript
    [void]$command.ExecuteNonQuery()

    #int test
    $testSw = [System.Diagnostics.StopWatch]::StartNew()
    Write-Host "Starting int test."
    $command.CommandText = "SELECT * FROM dbo.Example;"
    $reader = $command.ExecuteReader()
    while($reader.Read()) {
        $values = $reader.GetValues($values)
    }
    $reader.Close()
    $testSw.Stop()
    Write-Host "Int test duration was $($testSw.Elapsed.ToString())"

    $connedtion.Close()

}
catch [Exception]
{
    throw
}
0 голосов
/ 09 мая 2018

Несмотря на то, что полный контекст вопроса (например, план выполнения, индексы) неизвестен, я хотел бы написать в качестве ответа довольно большой список ошибок, связанных с GUID.

Все столбцы в вашей таблице имеют GUID.

  StaticId uniqueidentifier NOT NULL,
  UserId uniqueidentifier NULL,
  UserGroupId uniqueidentifier NULL,
  CompanyId uniqueidentifier NULL,
  CompanyGroupId uniqueidentifier NULL,
  CompanyAccessUnitRole uniqueidentifier NULL

Чтобы процитировать минусы из источника , где автор предпочитает GUID

GUID Минусы

  1. Это в 4 раза больше, чем у традиционного 4-байтового значения индекса; это может иметь серьезные последствия для производительности и хранения, если ты не осторожен
  2. Громоздко для отладки, когда userid = '{BAE7DF4-DDF-3RG-5TY3E3RF456AS10}'
  3. Сгенерированные идентификаторы GUID должны быть частично последовательными для лучшей производительности (например, newsequentialid () в SQL 2005) и для возможности использования кластерные индексы

Ваши данные будут распределены по большему количеству страниц и будут иметь больше физических чтений по сравнению с использованием, скажем, int для Key.

Если вы выполняете много операций вставки / обновления / удаления, ваши индексы будут сильно фрагментированы. Это связано с тем, что идентификаторы GUID генерируются случайным образом, и движок требует обновления для обновления индекса, чтобы организовать их в последовательном порядке.

Могу поспорить, что ваши индексы необходимо перестроить. Вот статья , которая сравнивает GUID с индексами столбцов INT и отражает, что GUID медленнее, чем INT, но может быть улучшен и приведен в соответствие при перестройке индекса.

Если вы считаете, что виновным является GUID, я бы посоветовал вам взглянуть на bigint в качестве опции

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