प्राकृतिक कुंजियाँ सरोगेट पूर्णांक कुंजियों की तुलना में SQL सर्वर में उच्च या निम्न प्रदर्शन प्रदान करती हैं?


25

मैं सरोगेट चाबियों का प्रशंसक हूं। एक जोखिम है मेरे निष्कर्षों की पुष्टि पक्षपाती है।

कई सवाल जो मैंने यहां और http://stackoverflow.com पर देखे हैं, वे IDENTITY()मूल्यों के आधार पर सरोगेट कुंजी के बजाय प्राकृतिक कुंजी का उपयोग करते हैं।

कंप्यूटर सिस्टम में मेरा बैकग्राउंड मुझे बताता है कि किसी पूर्णांक पर कोई तुलनात्मक संचालन करना स्ट्रिंग्स की तुलना करने से अधिक तेज़ होगा।

इस टिप्पणी ने मुझे मेरी मान्यताओं पर सवाल खड़ा किया, इसलिए मैंने सोचा कि मैं अपनी थीसिस की जांच करने के लिए एक प्रणाली बनाऊंगा कि पूर्णांक SQL सर्वर में कुंजियों के रूप में उपयोग के लिए तार से तेज हैं।

चूंकि छोटे डेटासेट में बहुत कम अंतर होने की संभावना है, इसलिए मैंने तुरंत एक दो टेबल सेटअप के बारे में सोचा, जहां प्राथमिक तालिका में 1,000,000 पंक्तियाँ हैं और माध्यमिक तालिका में कुल पंक्तियों में 10,000,000 पंक्तियों के लिए प्रत्येक पंक्ति में 10 पंक्तियाँ हैं। माध्यमिक तालिका। मेरे परीक्षण का आधार इस तरह की तालिका के दो सेट बनाना है, एक प्राकृतिक कुंजियों का उपयोग करना और एक पूर्णांक कुंजियों का उपयोग करना, और एक साधारण क्वेरी पर समय परीक्षण चलाना जैसे:

SELECT *
FROM Table1
    INNER JOIN Table2 ON Table1.Key = Table2.Key;

निम्नलिखित कोड मैं एक परीक्षण बिस्तर के रूप में बनाया गया है:

USE Master;
IF (SELECT COUNT(database_id) FROM sys.databases d WHERE d.name = 'NaturalKeyTest') = 1
BEGIN
    ALTER DATABASE NaturalKeyTest SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE NaturalKeyTest;
END
GO
CREATE DATABASE NaturalKeyTest 
    ON (NAME = 'NaturalKeyTest', FILENAME = 
        'C:\SQLServer\Data\NaturalKeyTest.mdf', SIZE=8GB, FILEGROWTH=1GB) 
    LOG ON (NAME='NaturalKeyTestLog', FILENAME = 
        'C:\SQLServer\Logs\NaturalKeyTest.mdf', SIZE=256MB, FILEGROWTH=128MB);
GO
ALTER DATABASE NaturalKeyTest SET RECOVERY SIMPLE;
GO
USE NaturalKeyTest;
GO
CREATE VIEW GetRand
AS 
    SELECT RAND() AS RandomNumber;
GO
CREATE FUNCTION RandomString
(
    @StringLength INT
)
RETURNS NVARCHAR(max)
AS
BEGIN
    DECLARE @cnt INT = 0
    DECLARE @str NVARCHAR(MAX) = '';
    DECLARE @RandomNum FLOAT = 0;
    WHILE @cnt < @StringLength
    BEGIN
        SELECT @RandomNum = RandomNumber
        FROM GetRand;
        SET @str = @str + CAST(CHAR((@RandomNum * 64.) + 32) AS NVARCHAR(MAX)); 
        SET @cnt = @cnt + 1;
    END
    RETURN @str;
END;
GO
CREATE TABLE NaturalTable1
(
    NaturalTable1Key NVARCHAR(255) NOT NULL 
        CONSTRAINT PK_NaturalTable1 PRIMARY KEY CLUSTERED 
    , Table1TestData NVARCHAR(255) NOT NULL 
);
CREATE TABLE NaturalTable2
(
    NaturalTable2Key NVARCHAR(255) NOT NULL 
        CONSTRAINT PK_NaturalTable2 PRIMARY KEY CLUSTERED 
    , NaturalTable1Key NVARCHAR(255) NOT NULL 
        CONSTRAINT FK_NaturalTable2_NaturalTable1Key 
        FOREIGN KEY REFERENCES dbo.NaturalTable1 (NaturalTable1Key) 
        ON DELETE CASCADE ON UPDATE CASCADE
    , Table2TestData NVARCHAR(255) NOT NULL  
);
GO

/* insert 1,000,000 rows into NaturalTable1 */
INSERT INTO NaturalTable1 (NaturalTable1Key, Table1TestData) 
    VALUES (dbo.RandomString(25), dbo.RandomString(100));
GO 1000000 

/* insert 10,000,000 rows into NaturalTable2 */
INSERT INTO NaturalTable2 (NaturalTable2Key, NaturalTable1Key, Table2TestData)
SELECT dbo.RandomString(25), T1.NaturalTable1Key, dbo.RandomString(100)
FROM NaturalTable1 T1
GO 10 

CREATE TABLE IDTable1
(
    IDTable1Key INT NOT NULL CONSTRAINT PK_IDTable1 
    PRIMARY KEY CLUSTERED IDENTITY(1,1)
    , Table1TestData NVARCHAR(255) NOT NULL 
    CONSTRAINT DF_IDTable1_TestData DEFAULT dbo.RandomString(100)
);
CREATE TABLE IDTable2
(
    IDTable2Key INT NOT NULL CONSTRAINT PK_IDTable2 
        PRIMARY KEY CLUSTERED IDENTITY(1,1)
    , IDTable1Key INT NOT NULL 
        CONSTRAINT FK_IDTable2_IDTable1Key FOREIGN KEY 
        REFERENCES dbo.IDTable1 (IDTable1Key) 
        ON DELETE CASCADE ON UPDATE CASCADE
    , Table2TestData NVARCHAR(255) NOT NULL 
        CONSTRAINT DF_IDTable2_TestData DEFAULT dbo.RandomString(100)
);
GO
INSERT INTO IDTable1 DEFAULT VALUES;
GO 1000000
INSERT INTO IDTable2 (IDTable1Key)
SELECT T1.IDTable1Key
FROM IDTable1 T1
GO 10

उपरोक्त कोड एक डेटाबेस और 4 टेबल बनाता है, और टेबल्स को डेटा के साथ भरता है, परीक्षण के लिए तैयार है। मेरे द्वारा चलाया गया परीक्षण कोड है:

USE NaturalKeyTest;
GO
DECLARE @loops INT = 0;
DECLARE @MaxLoops INT = 10;
DECLARE @Results TABLE (
    FinishedAt DATETIME DEFAULT (GETDATE())
    , KeyType NVARCHAR(255)
    , ElapsedTime FLOAT
);
WHILE @loops < @MaxLoops
BEGIN
    DBCC FREEPROCCACHE;
    DBCC FREESESSIONCACHE;
    DBCC FREESYSTEMCACHE ('ALL');
    DBCC DROPCLEANBUFFERS;
    WAITFOR DELAY '00:00:05';
    DECLARE @start DATETIME = GETDATE();
    DECLARE @end DATETIME;
    DECLARE @count INT;
    SELECT @count = COUNT(*) 
    FROM dbo.NaturalTable1 T1
        INNER JOIN dbo.NaturalTable2 T2 ON T1.NaturalTable1Key = T2.NaturalTable1Key;
    SET @end = GETDATE();
    INSERT INTO @Results (KeyType, ElapsedTime)
    SELECT 'Natural PK' AS KeyType, CAST((@end - @start) AS FLOAT) AS ElapsedTime;

    DBCC FREEPROCCACHE;
    DBCC FREESESSIONCACHE;
    DBCC FREESYSTEMCACHE ('ALL');
    DBCC DROPCLEANBUFFERS;
    WAITFOR DELAY '00:00:05';
    SET @start = GETDATE();
    SELECT @count = COUNT(*) 
    FROM dbo.IDTable1 T1
        INNER JOIN dbo.IDTable2 T2 ON T1.IDTable1Key = T2.IDTable1Key;
    SET @end = GETDATE();
    INSERT INTO @Results (KeyType, ElapsedTime)
    SELECT 'IDENTITY() PK' AS KeyType, CAST((@end - @start) AS FLOAT) AS ElapsedTime;

    SET @loops = @loops + 1;
END
SELECT KeyType, FORMAT(CAST(AVG(ElapsedTime) AS DATETIME), 'HH:mm:ss.fff') AS AvgTime 
FROM @Results
GROUP BY KeyType;

ये परिणाम हैं:

यहाँ छवि विवरण दर्ज करें

क्या मैं यहाँ कुछ गलत कर रहा हूँ, या INT 25 अक्षर प्राकृतिक कुंजियों की तुलना में 3 गुना तेज हैं?

ध्यान दें, मैंने यहाँ एक अनुवर्ती प्रश्न लिखा है


1
अच्छी तरह से INT 4 बाइट्स है और प्रभावी NVARCHAR (25) लगभग 14 गुना लंबा (सिस्टम डेटा जैसे लंबाई) सहित है, इसलिए अकेले सूचकांक के संदर्भ में मेरा मानना ​​है कि आपके पास काफी व्यापक और गहरा पीके इंडेक्स होगा और इसलिए अधिक मैं / O की आवश्यकता है जो प्रसंस्करण समय को प्रभावित करेगा। Howevev एक प्राकृतिक पूर्णांक (शायद यहां तक ​​कि डिजीटल चेक) भी बहुत INT होगा जो हम एक सरोगेट आइडेंटिटी कॉलम के लिए उपयोग करने के बारे में सोचते हैं। तो, "प्राकृतिक कुंजी" शायद एक INT, BIGINT, CHAR, NVARCHAR और वह सब मायने रखती है।
RLF

7
मुझे लगता है कि @ माइकशेरिल'कैटकॉल 'पर प्रदर्शन लाभ यह हो रहा था कि जब आप एक प्राकृतिक कुंजी का उपयोग करते हैं, तो आपको वास्तव में "लुकअप" तालिका में शामिल होने की आवश्यकता नहीं होती है। लुकअप मान को ज्वाइन करने के लिए एक क्वेरी से तुलना करें, एक क्वेरी के साथ जहां मूल्य पहले से ही मुख्य तालिका में संग्रहीत है। लुकअप तालिका में प्राकृतिक कुंजी लंबाई और पंक्तियों की संख्या के आधार पर आपको एक अलग "विजेता" मिल सकता है।
मिकेल एरिकसन

3
क्या @MikaelEriksson ने कहा कि ऐसे मामले जब आपके पास 2 से अधिक तालिकाओं (4) के बीच एक जुड़ाव है, जहां सरोगेट्स के साथ आपको B और C के माध्यम से A से D तक तालिका में शामिल होना पड़ेगा जबकि प्राकृतिक कुंजियों के साथ आप A से D में सीधे जुड़ सकते हैं।
ypercube y

जवाबों:


18

सामान्य तौर पर, SQL सर्वर अनुक्रमित के लिए B + Trees का उपयोग करता है। एक सूचकांक की तलाश का खर्च सीधे इस भंडारण प्रारूप में कुंजी की लंबाई से संबंधित है। इसलिए, एक सरोगेट कुंजी आमतौर पर सूचकांक की तलाश में एक प्राकृतिक कुंजी को बेहतर बनाती है।

SQL सर्वर प्राथमिक रूप से डिफ़ॉल्ट रूप से एक टेबल को क्लस्टर करता है। क्लस्टर इंडेक्स कुंजी का उपयोग पंक्तियों की पहचान करने के लिए किया जाता है, इसलिए इसे हर दूसरे इंडेक्स में शामिल कॉलम के रूप में जोड़ा जाता है। व्यापक कि कुंजी, बड़ा हर माध्यमिक सूचकांक।

इससे भी बदतर, अगर माध्यमिक सूचकांक स्पष्ट रूप UNIQUEसे परिभाषित नहीं किए जाते हैं क्योंकि क्लस्टर इंडेक्स कुंजी स्वचालित रूप से उनमें से प्रत्येक की कुंजी का हिस्सा बन जाती है। यह आमतौर पर अधिकांश अनुक्रमितों पर लागू होता है, क्योंकि आमतौर पर अनुक्रमित को केवल तभी अद्वितीय घोषित किया जाता है जब आवश्यकता विशिष्टता को लागू करने के लिए होती है।

इसलिए यदि प्रश्न प्राकृतिक बनाम सरोगेट क्लस्टर इंडेक्स है, तो सरोगेट लगभग हमेशा जीत जाएगा।

दूसरी ओर, आप उस सरोगेट स्तंभ को तालिका में जोड़ रहे हैं जिससे तालिका अपने आप में बड़ी हो जाती है। यह अधिक महंगा पाने के लिए क्लस्टर इंडेक्स स्कैन का कारण बनेगा। इसलिए, यदि आपके पास केवल कुछ ही माध्यमिक सूचकांक हैं और आपके कार्यभार को अक्सर सभी (या अधिकांश) पंक्तियों को देखने की आवश्यकता होती है, तो आप वास्तव में उन कुछ अतिरिक्त बाइट्स को बचाने के लिए एक प्राकृतिक कुंजी के साथ बेहतर हो सकते हैं।

अंत में, प्राकृतिक कुंजी अक्सर डेटा मॉडल को समझना आसान बना देती है। अधिक संग्रहण स्थान का उपयोग करते समय, प्राकृतिक प्राथमिक कुंजी प्राकृतिक विदेशी कुंजी को जन्म देती है जो बदले में स्थानीय सूचना घनत्व को बढ़ाती है।

इसलिए, जैसा कि अक्सर डेटाबेस की दुनिया में होता है, असली जवाब "यह निर्भर करता है" है। और - हमेशा यथार्थवादी डेटा के साथ अपने स्वयं के वातावरण में परीक्षण करें।


10

मेरा मानना ​​है, कि सबसे अच्छा बीच में है

प्राकृतिक कुंजी अवलोकन:

  1. वे डेटा मॉडल को अधिक स्पष्ट बनाते हैं क्योंकि वे विषय क्षेत्र से आते हैं, न कि किसी के सिर से।
  2. साधारण कुंजी (एक कॉलम, बीच CHAR(4)और CHAR(20)) कुछ अतिरिक्त बाइट्स बचा रहे हैं, लेकिन आपको उनकी स्थिरता के लिए देखने की जरूरत है ( ON UPDATE CASCADEउन कुंजियों के लिए महत्वपूर्ण हो जाता है, जिन्हें बदला जा सकता है)।
  3. बहुत सारे मामले, जब प्राकृतिक कुंजी जटिल होती है: दो या अधिक कॉलम होते हैं। यदि ऐसी कुंजी किसी अन्य इकाई को फ़ोरिंग कुंजी के रूप में माइग्रेट कर सकती है, तो यह डेटा ओवरहेड जोड़ देगा (सूचक और डेटा कॉलम बड़े हो सकते हैं) और प्रदर्शन ढीला।
  4. यदि कुंजी एक बड़ी स्ट्रिंग है, तो यह हमेशा पूर्णांक कुंजी के लिए ढीली होगी, क्योंकि साधारण खोज स्थिति डेटाबेस इंजन में एक बाइट सरणी तुलना में बदल जाती है, जो कि ज्यादातर मामलों में पूर्णांक तुलना की तुलना में धीमी होती है।
  5. यदि कुंजी एक बहुभाषी स्ट्रिंग है, तो कोलाज को भी देखना होगा।

लाभ: 1 और 2।

वॉचआउट: 3, 4 और 5।


कृत्रिम पहचान कुंजी अवलोकन:

  1. डेटाबेस इंजन द्वारा नियंत्रित इस सुविधा के रूप में आपको उनके निर्माण और हैंडलिंग (ज्यादातर मामलों में) के बारे में परेशान होने की आवश्यकता नहीं है। वे डिफ़ॉल्ट रूप से अद्वितीय हैं और बहुत अधिक स्थान नहीं लेते हैं। कस्टम ऑपरेशंस जैसे ON UPDATE CASCADEकि ommited हो सकते हैं, क्योंकि प्रमुख मान नहीं बदल रहे हैं।

  2. वे (अक्सर) विदेशी कुंजी के रूप में प्रवास के लिए सर्वश्रेष्ठ उम्मीदवार होते हैं क्योंकि:

    2.1। एक स्तंभ के होते हैं;

    2.2। एक सरल प्रकार का उपयोग करना जिसमें एक छोटा वजन होता है और तुलनात्मक संचालन के लिए तेजी से कार्य करता है।

  3. एक एसोसिएशन संस्थाओं के लिए, जो कुंजी कहीं भी माइग्रेट नहीं होती हैं, यह एक शुद्ध डेटा ओवरहेड बन सकती है, क्योंकि यह उपयोगिता खो जाती है। जटिल प्राकृतिक प्राथमिक कुंजी (यदि वहां कोई स्ट्रिंग कॉलम नहीं हैं) अधिक उपयोगी होगा।

लाभ: 1 और 2।

वॉचआउट: 3


निष्कर्ष:

कृत्रिम कुंजी अधिक रखरखाव योग्य, विश्वसनीय और तेज़ हैं क्योंकि उन्हें इस सुविधाओं के लिए डिज़ाइन किया गया है। लेकिन कुछ मामलों में जरूरत नहीं है। उदाहरण के लिए, CHAR(4)ज्यादातर मामलों में एकल स्तंभ उम्मीदवार जैसा व्यवहार करता है INT IDENTITY। तो यहाँ एक और सवाल यह भी है: रख-रखाव + स्थिरता या प्रत्यक्षता ?

सवाल "क्या मुझे एक कृत्रिम कुंजी इंजेक्ट करनी चाहिए या नहीं?" हमेशा प्राकृतिक कुंजी संरचना पर निर्भर करता है :

  • यदि इसमें एक बड़ी स्ट्रिंग है, तो यह धीमी है और किसी अन्य इकाई के लिए विदेशी के रूप में माइग्रेट करने पर डेटा ओवरहेड जोड़ देगा।
  • यदि इसमें कई कॉलम हैं, तो यह धीमा है और किसी अन्य इकाई में विदेशी के रूप में माइग्रेट होने पर डेटा ओवरहेड जोड़ देगा।

5
"कस्टम संचालन जैसे कस्टम संचालन को बदल दिया जा सकता है, क्योंकि प्रमुख मूल्य नहीं बदल रहे हैं।" सरोगेट कुंजी का प्रभाव प्रत्येक विदेशी कुंजी संदर्भ को "ON UPDATE CASCADE" के समतुल्य बनाना है। कुंजी परिवर्तित नहीं होती है, लेकिन वह मान जिसका प्रतिनिधित्व करता है
माइक शेरिल 'कैट रिकॉल'

@ माइकशेरिल'काटकल 'हां, बिल्कुल। हालाँकि, ON UPDATE CASCADEउपयोग नहीं किया गया, जबकि कुंजियाँ कभी भी अपडेट नहीं की गईं। लेकिन, अगर वे हैं, तो यह एक समस्या हो सकती है यदि ON UPDATE NO ACTIONकॉन्फ़िगर किया गया है। मेरा मतलब है, कि DBMS इसका उपयोग कभी नहीं करते हैं, जबकि प्रमुख स्तंभ मान नहीं बदले हैं।
ब्लिटज सिप

4

एक कुंजी डेटाबेस की एक तार्किक विशेषता है, जबकि प्रदर्शन हमेशा भंडारण में भौतिक कार्यान्वयन और उस कार्यान्वयन के खिलाफ चलने वाले भौतिक कार्यों द्वारा निर्धारित किया जाता है। इसलिए प्रदर्शन विशेषताओं को कुंजियों में बदलना एक गलती है।

हालांकि इस विशेष उदाहरण में, तालिकाओं और प्रश्नों के दो संभावित कार्यान्वयन एक दूसरे की तुलना में हैं। उदाहरण यहां शीर्षक में प्रस्तुत किए जा रहे प्रश्न का उत्तर नहीं देता है। जो तुलना की जा रही है वह सिर्फ एक प्रकार के इंडेक्स (बी-ट्री) का उपयोग करके दो अलग-अलग डेटाटिप्स (पूर्णांक और चरित्र) का उपयोग करने वाली जॉइन की है। एक "स्पष्ट" बिंदु यह है कि अगर हैश इंडेक्स या अन्य प्रकार के इंडेक्स का उपयोग किया गया है, तो संभवतः दो कार्यान्वयन के बीच कोई औसत दर्जे का प्रदर्शन अंतर नहीं होगा। उदाहरण के साथ और भी मूलभूत समस्याएं हैं।

प्रदर्शन के लिए दो प्रश्नों की तुलना की जा रही है लेकिन दो प्रश्न तार्किक रूप से समतुल्य नहीं हैं क्योंकि वे अलग-अलग परिणाम देते हैं! एक अधिक यथार्थवादी परीक्षण दो प्रश्नों की तुलना करेगा समान लौटाएगा परिणाम पर लौटाएगा लेकिन विभिन्न कार्यान्वयन का उपयोग करेगा।

सरोगेट कुंजी के बारे में आवश्यक बिंदु यह है कि यह एक तालिका में एक अतिरिक्त विशेषता है जहां तालिका में व्यावसायिक डोमेन में उपयोग की गई "सार्थक" प्रमुख विशेषताएं भी हैं। यह गैर-सरोगेट विशेषता है जो उपयोगी होने के लिए क्वेरी परिणामों के लिए रुचि रखते हैं। एक यथार्थवादी परीक्षण इसलिए एक ही तालिका में दोनों प्राकृतिक और सरोगेट कुंजी होने के वैकल्पिक कार्यान्वयन के साथ केवल प्राकृतिक कुंजी का उपयोग कर तालिकाओं की तुलना करेंगे । सरोगेट कुंजी को आम तौर पर अतिरिक्त भंडारण और अनुक्रमण की आवश्यकता होती है और परिभाषा के अनुसार अतिरिक्त विशिष्टता बाधाओं की आवश्यकता होती है। सरोगेट्स को अपने सरोगेट्स और इसके विपरीत बाहरी प्राकृतिक कुंजी मूल्यों को मैप करने के लिए अतिरिक्त प्रसंस्करण की आवश्यकता होती है।

अब इस संभावित क्वेरी की तुलना करें:

ए।

SELECT t2.NaturalTable2Key, t2.NaturalTable1Key
FROM Table2 t2;

यदि तालिका 2 में NaturalTable1Key विशेषता सरोगेट IDTable1Key के साथ प्रतिस्थापित है, तो इसके तार्किक समकक्ष के लिए:

बी

SELECT t2.NaturalTable2Key, t1.NaturalTable1Key
FROM Table2 t2
INNER JOIN Table1 t1
ON t1.IDTable1Key = t2.IDTable1Key;

क्वेरी बी में एक शामिल होने की आवश्यकता है; क्वेरी A नहीं करता है। यह डेटाबेस में एक परिचित स्थिति है जो (ओवर) सरोगेट्स का उपयोग करती है। अनुकूलन करने के लिए क्वेरी अनावश्यक रूप से जटिल और बहुत कठिन हो जाती हैं। व्यावसायिक तर्क (विशेषकर डेटा अखंडता की कमी) को लागू करना, परीक्षण करना और सत्यापित करना अधिक कठिन हो जाता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.