Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

SQL Server 2016:Въздействие върху производителността на винаги криптирани

Като част от T-SQL Tuesday #69, писах в блог за ограниченията на Always Encrypted и споменах там, че производителността може да бъде отрицателно повлияна от използването му (както може да очаквате, по-силната сигурност често има компромиси). В тази публикация исках да разгледам набързо това, като имам предвид (отново), че тези резултати са базирани на CTP 2.2 код, толкова рано в цикъла на разработка, и не отразяват непременно производителността, която ще вижте RTM.

Първо, исках да демонстрирам, че Always Encrypted работи от клиентски приложения, дори ако най-новата версия на SQL Server 2016 не е инсталирана там. Все пак трябва да инсталирате визуализацията на .NET Framework 4.6 (най-новата версия тук и това може да се промени), за да поддържате Column Encryption Setting атрибут низ на свързване. Ако използвате Windows 10 или сте инсталирали Visual Studio 2015, тази стъпка не е необходима, тъй като вече трябва да имате достатъчно нова версия на .NET Framework.

След това трябва да се уверите, че винаги криптираният сертификат съществува на всички клиенти. Вие създавате главния и колонния ключ за криптиране в базата данни, както ще ви покаже всеки урок за Always Encrypted, след което трябва да експортирате сертификата от тази машина и да го импортирате на другите, където ще се изпълнява кодът на приложението. Отворете certmgr.msc и разгънете Сертификати – Текущ потребител> Лични> Сертификати и там трябва да има един, наречен Always Encrypted Certificate . Щракнете с десния бутон върху него, изберете Всички задачи> Експортиране и следвайте подканите. Експортирах частния ключ и предоставих парола, което създаде .pfx файл. След това просто повтаряте обратния процес на клиентските машини:Отворете certmgr.msc , разгънете Сертификати – Текущ потребител> Лични, щракнете с десния бутон на мишката, изберете Всички задачи> Импортиране и го насочете към .pfx файла, който сте създали по-горе. (Официална помощ тук.)

(Има по-сигурни начини за управление на тези сертификати – не е вероятно просто да разположите сертификата по този начин на всички машини, тъй като скоро ще се запитате какъв беше смисълът? Правех това само в моята изолирана среда за целите на тази демонстрация – исках да се уверя, че приложението ми извлича данни по кабела, а не само в локалната памет.)

Създаваме две бази данни, едната с криптирана таблица и една без. Правим това, за да изолираме низовете за свързване, а също и да измерим използването на пространството. Разбира се, има по-подробни начини да контролирате кои команди трябва да използват връзка с активирано криптиране – вижте бележката, озаглавена „Контролиране на въздействието върху производителността…“ в тази статия.

Таблиците изглеждат така:

-- криптирано копие, в базата данни Шифровано CREATE TABLE dbo.Employees( ID INT IDENTITY(1,1) ПЪРВИЧЕН КЛЮЧ, Фамилия NVARCHAR(32) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (ENCRYPTION_TYPE =DETERMINATIONS_6_CHAECHA_6_5_DETERMINATION_6_CHE_2CHEAD_BIN_5_5_DETERMIN_CHE_2_CHE_2CHEAD_BIN2) ColumnKey) НЕ НУЛЕВО, заплата INT Е КРИПТИРАН С (ENCRYPTION_TYPE =RANDOMIZED, ALGORITHM ='AEAD_AES_256_CBC_HMAC_SHA_256', COLUMN_ENCRYPTION_KEY =NOTKOLUMnKey)); -- нешифровано копие, в базата данни Нормално CREATE TABLE dbo.Employees( ID INT IDENTITY(1,1) ПЪРВИЧЕН КЛЮЧ, Фамилия NVARCHAR(32) COLLATE Latin1_General_BIN2 NOT NULL, Salary INT NOT NULL);

С тези таблици на място исках да настроя много просто приложение от командния ред, което да изпълнява следните задачи срещу криптираната и некриптираната версия на таблицата:

  • Вмъкнете 100 000 служители, един по един
  • Прочетете 100 произволни реда, 1000 пъти
  • Извеждане на времеви печати преди и след всяка стъпка

Така че имаме съхранена процедура в напълно отделна база данни, използвана за производство на произволни цели числа за представяне на заплати и произволни низове в Unicode с различна дължина. Ще правим това едно по едно, за да симулирам по-добре реално използване на 100 000 вмъквания, които се случват независимо (макар и не едновременно, тъй като не съм достатъчно смел да се опитам правилно да разработя и управлявам многонишково C# приложение или да се опитам да координирам и синхронизирайте множество екземпляри на едно приложение).

CREATE DATABASE Utility;GO USE Utility;GO CREATE PROCEDURE dbo.GenerateNameAndSalary @Name NVARCHAR(32) OUTPUT, @Salary INT OUTPUTASBEGIN SET NOCOUNT ON; SELECT @Name =LEFT(CONVERT(NVARCHAR(32), CRYPT_GEN_RANDOM(64)), RAND() * 32 + 1); ИЗБЕРЕТЕ @Заплата =CONVERT(INT, RAND()*100000)/100*100;ENDGO

Няколко реда примерен изход (не ни интересува действителното съдържание на низа, само че то варира):

酹2׿ዌ륒㦢㮧羮怰㉤盿⚉嗝䬴敏⽁캘♜鼹䓧98600 贓峂쌄탠❼缉腱蛽>20 прер. 

Тогава съхранените процедури, които приложението в крайна сметка ще извика (те са идентични и в двете бази данни, тъй като вашите заявки не трябва да се променят, за да поддържат Always Encrypted):

СЪЗДАВАНЕ НА ПРОЦЕДУРА dbo.AddPerson @LastName NVARCHAR(32), @Salary INTASBEGIN SET NOCOUNT ON; INSERT dbo.Employees(LastName, Salary) SELECT @LastName, @Salary;ENDGO СЪЗДАВАНЕ НА ПРОЦЕДУРА dbo.RetrievePeopleASBEGIN SET NOCOUNT ON; ИЗБЕРЕТЕ ТОП (100) ИД, Фамилия, Заплата ОТ dbo. Служители ПОРЪЧАЙТЕ ПО NEWID();ENDGO

Сега кодът на C#, започващ с частта connectionStrings на App.config. Важната част е Column Encryption Setting опция само за базата данни с криптирани колони (за краткост приемете, че и трите низа за връзка съдържат един и същ Data Source , и същото SQL удостоверяване User ID и Password ):

   

И Program.cs (съжалявам, за демонстрации като тази, аз съм ужасен да влизам и да преименувам нещата логично):

използвайки System;използвайки System.Collections.Generic;използвайки System.Text;използвайки System.Configuration;използвайки System.Data;използвайки System.Data.SqlClient; namespace AEDemo{ class Program { static void Main(string[] args) { using (SqlConnection con1 =new SqlConnection()) { Console.WriteLine(DateTime.UtcNow.ToString("hh:mm:ss.fffffff")); име на низ; низ EmptyString =""; интерна заплата; int i =1; while (i <=100000) { con1.ConnectionString =ConfigurationManager.ConnectionStrings["Utility"].ToString(); използване (SqlCommand cmd1 =new SqlCommand("dbo.GenerateNameAndSalary", con1)) { cmd1.CommandType =CommandType.StoredProcedure; SqlParameter n =нов SqlParameter("@Name", SqlDbType.NVarChar, 32) { Direction =ParameterDirection.Output}; SqlParameter s =нов SqlParameter("@Salary", SqlDbType.Int) { Direction =ParameterDirection.Output}; cmd1.Parameters.Add(n); cmd1.Parameters.Add(s); con1.Open(); cmd1.ExecuteNonQuery(); име =n.Value.ToString(); заплата =Convert.ToInt32(s.Value); con1.Close(); } използване (SqlConnection con2 =new SqlConnection()) { con2.ConnectionString =ConfigurationManager.ConnectionStrings[args[0]].ToString(); използване (SqlCommand cmd2 =new SqlCommand("dbo.AddPerson", con2)) { cmd2.CommandType =CommandType.StoredProcedure; SqlParameter n =нов SqlParameter("@LastName", SqlDbType.NVarChar, 32); SqlParameter s =нов SqlParameter("@Salary", SqlDbType.Int); n.Стойност =име; s.Value =заплата; cmd2.Параметри.Добавяне(n); cmd2.Parameters.Add(s); con2.Open(); cmd2.ExecuteNonQuery(); con2.Close(); } } i++; } Console.WriteLine(DateTime.UtcNow.ToString("hh:mm:ss.fffffff")); i =1; while (i <=1000) { using (SqlConnection con3 =new SqlConnection()) { con3.ConnectionString =ConfigurationManager.ConnectionStrings[args[0]].ToString(); използване (SqlCommand cmd3 =new SqlCommand("dbo.RetrievePeople", con3)) { cmd3.CommandType =CommandType.StoredProcedure; con3.Open(); SqlDataReader rdr =cmd3.ExecuteReader(); while (rdr.Read()) { EmptyString +=rdr[0].ToString(); } con3.Close(); } } i++; } Console.WriteLine(DateTime.UtcNow.ToString("hh:mm:ss.fffffff")); } } }}

След това можем да извикаме .exe със следните командни редове:

AEDemoConsole.exe "Normal"AEDemoConsole.exe "Encrypt"

И ще произведе три изходни реда за всяко повикване:началния час, времето след вмъкване на 100 000 реда и времето след четене на 100 реда 1000 пъти. Ето резултатите, които видях на моята система, средно за 5 проби всеки:

Продължителност (секунди) на запис и четене на данни

Има ясно въздействие върху записването на данните – не съвсем 2X, но повече от 1,5X. Имаше много по-ниска делта при четене и декриптиране на данните – поне в тези тестове – но и това не беше безплатно.

Що се отнася до използването на пространството, има приблизително 3X наказание за съхранение на криптирани данни (предвид естеството на повечето алгоритми за криптиране, това не трябва да е шокиращо). Имайте предвид, че това беше на маса само с един клъстериран първичен ключ. Ето и цифрите:

Пространство (MB), използвано за съхранение на данни

Така че очевидно има някои санкции при използването на Always Encrypted, както обикновено има с почти всички решения, свързани със сигурността (поговорката „без безплатен обяд“ идва на ум). Ще повторя, че тези тестове бяха извършени срещу CTP 2.2, който може да е коренно различен от окончателното издание на SQL Server 2016. Освен това тези разлики, които наблюдавах, може да отразяват само естеството на тестовете, които измислих; очевидно се надявам да използвате този подход, за да тествате резултатите си спрямо вашата схема, на вашия хардуер и с вашите модели за достъп до данни.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Получаване на данни от съхранена процедура с Entity Framework

  2. как да направите данните си хоризонтални

  3. SQL Server BIT Datatype – Крайно ръководство

  4. Избягване на SQL инжектиране без параметри

  5. как да присвоите стойност на cte на променлива