ما هي تحسينات أداء الدليل التسلسلي مقارنة بالدليل القياسي؟

StackOverflow https://stackoverflow.com/questions/170346

  •  05-07-2019
  •  | 
  •  

سؤال

هل قام شخص ما بقياس أداء Sequential Guid مقابل.المرشد القياسي عند استخدامه كمفاتيح أساسية داخل قاعدة البيانات؟

هل كانت مفيدة؟

المحلول

GUID مقابل GUID المتسلسل



النمط النموذجي هو استخدام Guid كـ PK للجداول، ولكن كما هو مشار إليه في مناقشات أخرى (انظر مزايا وعيوب مفاتيح قاعدة البيانات GUID / UUID) هناك بعض مشكلات الأداء.

هذا تسلسل إرشادي نموذجي

f3818d69-2552-40b7-a403-01a6db4552f7
7ce31615-فاف-42c4-b317-40d21a6a3c60
94732fc7-768e-4cf2-9107-f0953f6795a5


مشاكل هذا النوع من البيانات هي:<
-

  • توزيعات واسعة للقيم
  • بشكل عشوائي تقريبًا
  • استخدام الفهرس سيء جدًا جدًا
  • الكثير من الأوراق تتحرك
  • كل ما يقرب من كل PK يحتاج إلى أن يكون على الأقل على فهرس غير مجموعات
  • تحدث المشكلة على كل من Oracle و SQL Server



الحل المحتمل هو استخدام Sequential Guid، الذي يتم إنشاؤه على النحو التالي:

cc6466f7-1066-11dd-acb6-005056c00008
cc6466f8-1066-11dd-acb6-005056c00008
cc6466f9-1066-11dd-acb6-005056c00008


كيفية توليدها من كود C#:

[DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(out Guid guid);

public static Guid SequentialGuid()
{
    const int RPC_S_OK = 0;
    Guid g;
    if (UuidCreateSequential(out g) != RPC_S_OK)
        return Guid.NewGuid();
    else
        return g;
}


فوائد

  • استخدام أفضل للمؤشر
  • السماح باستخدام المفاتيح المجمعة (سيتم التحقق منها في سيناريوهات NLB)
  • استخدام أقل للقرص
  • 20-25 ٪ من زيادة الأداء بأقل تكلفة



قياس الحياة الحقيقية:سيناريو:

  • GUID مخزنة كنوع فريد من نوعها على خادم SQL
  • تم تخزين الدليل كـ CHAR(36) في Oracle
  • الكثير من عمليات إدراج ، مزجدة معًا في صفقة واحدة
  • من 1 إلى 100s من الإدراج اعتمادًا على الجدول
  • بعض الجداول> 10 ملايين صف



الاختبار المعملي – SQL Server

اختبار VS2008، 10 مستخدمين متزامنين، بدون وقت للتفكير، عملية قياس الأداء مع 600 إدراج دفعة واحدة لجدول الأوراق
الدليل القياسي
متوسطمدة العملية: 10.5 ثانية
متوسططلب للمرة الثانية: 54.6
متوسطالرد.وقت: 0.26

دليل متسلسل
متوسطمدة العملية: 4.6 ثانية
متوسططلب للمرة الثانية: 87.1
متوسطالرد.وقت: 0.12

النتائج على أوراكل (عذرًا، تم استخدام أداة مختلفة للاختبار) 1.327.613 أدخل على طاولة مع دليل PK

الدليل القياسي, 0.02 ثانية.الوقت المنقضي لكل إدراج، 2.861 ثانية.من وقت وحدة المعالجة المركزية، إجمالي 31.049 ثانية.انقضى

دليل متسلسل, 0.00 ثانية.الوقت المنقضي لكل إدراج، 1.142 ثانية.من وقت وحدة المعالجة المركزية، إجمالي 3.667 ثانية.انقضى

لقد مر وقت انتظار القراءة التسلسلية لملف قاعدة البيانات 6.4 الملايين ينتظرون الأحداث 62.415 ثواني ل 1.2 مليون انتظر الأحداث 11.063 ثواني.

من المهم أن نرى أنه يمكن تخمين جميع الأدلة التسلسلية، لذلك ليس من الجيد استخدامها إذا كان الأمان يمثل مصدر قلق، مع الاستمرار في استخدام الدليل القياسي.
لكي أختصر...إذا كنت تستخدم Guid كـ PK، فاستخدم المرشد المتسلسل في كل مرة لا يتم تمريرها ذهابًا وإيابًا من واجهة المستخدم، فسوف تعمل على تسريع العملية ولن تكلف أي شيء للتنفيذ.

نصائح أخرى

ربما أفتقد شيئًا ما هنا (لا تتردد في تصحيحي إذا كنت كذلك)، ولكن لا يمكنني رؤية فائدة تذكر في استخدام GUID/UUIDs التسلسلية للمفاتيح الأساسية.

ال نقطة استخدام المعرفات الفريدة العمومية (GUIDs) أو UUIDs عبر الأعداد الصحيحة المتزايدة تلقائيًا هو:

  • يمكن إنشاؤها في أي مكان بدون الاتصال بقاعدة البيانات
  • إنها معرفات فريدة تمامًا داخل التطبيق الخاص بك (وفي حالة UUIDs، تكون فريدة عالميًا)
  • نظرًا لمعرف واحد، لا توجد طريقة لتخمين المعرف التالي أو السابق (أو حتى أي معرفات صالحة أخرى) خارج نطاق القوة الغاشمة أ ضخم مساحة المفاتيح.

لسوء الحظ، باستخدام اقتراحك، تخسر الجميع هذه الأشياء.

لذا، نعم.لقد قمت بتحسين المعرفات الفريدة العمومية (GUIDs).ولكن في هذه العملية، تكون قد تخلصت تقريبًا من جميع الأسباب التي دفعتك لاستخدامها في المقام الأول.

اذا أنت حقًا إذا كنت ترغب في تحسين الأداء، فاستخدم مفتاحًا أساسيًا قياسيًا للتزايد التلقائي لعدد صحيح.يوفر ذلك جميع المزايا التي وصفتها (وأكثر) مع كونه أفضل من "الدليل المتسلسل" بكل الطرق تقريبًا.

من المرجح أن يتم نسيان هذا لأنه لا يجيب على سؤالك على وجه التحديد (والذي يبدو أنه تمت صياغته بعناية حتى تتمكن من الإجابة عليه بنفسك على الفور)، لكنني أشعر أنها نقطة أكثر أهمية بكثير يجب إثارتها.

كما قال ماسيموجينتيليني بالفعل، يمكن تحسين الأداء عند استخدام UuidCreateSequential (عند إنشاء الأدلة في التعليمات البرمجية).ولكن يبدو أن الحقيقة مفقودة:يستخدم SQL Server (على الأقل Microsoft SQL 2005/2008) نفس الوظيفة، ولكن:تختلف مقارنة/ترتيب الأدلة الإرشادية في .NET وفي SQL Server، الأمر الذي قد يتسبب في المزيد من عمليات الإدخال والإخراج، لأنه لن يتم ترتيب الأدلة الإرشادية بشكل صحيح.من أجل إنشاء الأدلة المطلوبة بشكل صحيح لخادم SQL (الطلب)، عليك القيام بما يلي (انظر مقارنة تفاصيل):

[System.Runtime.InteropServices.DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(byte[] buffer);

static Guid NewSequentialGuid() {

    byte[] raw = new byte[16];
    if (UuidCreateSequential(raw) != 0)
        throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());

    byte[] fix = new byte[16];

    // reverse 0..3
    fix[0x0] = raw[0x3];
    fix[0x1] = raw[0x2];
    fix[0x2] = raw[0x1];
    fix[0x3] = raw[0x0];

    // reverse 4 & 5
    fix[0x4] = raw[0x5];
    fix[0x5] = raw[0x4];

    // reverse 6 & 7
    fix[0x6] = raw[0x7];
    fix[0x7] = raw[0x6];

    // all other are unchanged
    fix[0x8] = raw[0x8];
    fix[0x9] = raw[0x9];
    fix[0xA] = raw[0xA];
    fix[0xB] = raw[0xB];
    fix[0xC] = raw[0xC];
    fix[0xD] = raw[0xD];
    fix[0xE] = raw[0xE];
    fix[0xF] = raw[0xF];

    return new Guid(fix);
}

أو هذا الرابط أو هذا الرابط.

اذا أنت يحتاج لاستخدام معرفات GUI متسلسلة، يمكن أن يقوم SQL Server 2005 بإنشائها لك باستخدام NEWSEQUENTIALID() وظيفة.

لكن نظرًا لأن الاستخدام الأساسي للمعرفات الفريدة العمومية (GUIds) هو إنشاء مفاتيح (أو مفاتيح بديلة) لا يمكن تخمينها (على سبيل المثال لتجنب تمرير الأشخاص للمفاتيح التي تم تخمينها على GETs)، فأنا لا أرى مدى قابليتها للتطبيق لأنه من السهل تخمينها.

من MSDN:

مهم:
إذا كانت الخصوصية مصدر قلق، فلا تستخدم هذه الوظيفة.من الممكن تخمين قيمة GUID التي تم إنشاؤها التالي ، وبالتالي الوصول إلى البيانات المرتبطة بهذا GUID.

وانظر هذا المقال: ( http://www.shirmanov.com/2010/05/generating- newsequentialid-compatible.html )

وعلى الرغم من MSSQL يستخدم هذه الوظيفة نفسها لتوليد NewSequencialIds (UuidCreateSequential (من ارشد ارشد))، MSSQL عكس 3 وأنماط بايت 4TH التي لا تعطيك نفس النتيجة التي ستحصل عند استخدام هذه الوظيفة في التعليمات البرمجية. يظهر Shirmanov كيفية الحصول على نفس النتائج الدقيقة التي MSSQL من شأنه أن يخلق.

أمشاط التي كتبها جيمي نيلسون: نوع من GUID حيث تم استبدال عدد من بت بقيمة يشبه الطابع الزمني. وهذا يعني أن أمشاط يمكن أن يؤمر، وعندما تستخدم نتيجة المفتاح الأساسي في أقل الانقسامات الصفحة المؤشر عند إدخال قيم جديدة.

هل هو موافق لاستخدام uniqueidentifier (GUID) كمفتاح أساسي؟

حسنًا، لقد وصلت أخيرًا إلى هذه النقطة في التصميم والإنتاج بنفسي.

أقوم بإنشاء COMB_GUID حيث تعتمد الـ 32 بت العليا على البتات من 33 إلى 1 من وقت Unix بالمللي ثانية.لذلك، هناك 93 بت من العشوائية كل 2 مللي ثانية، ويحدث التمرير على البتات العليا كل 106 سنوات.التمثيل الفعلي لـ COMB_GUID (أو النوع 4 UUID) هو نسخة مشفرة من نوع base64 مكونة من 128 بت، وهي عبارة عن سلسلة مكونة من 22 حرفًا.

عند الإدراج في postgres، تكون نسبة السرعة بين UUID العشوائي بالكامل وCOMB _GUID مفيدة لـ COMB_GUID.COMB_GUID هو 2X أسرع على أجهزتي عبر اختبارات متعددة، لاختبار مليون سجل.تحتوي السجلات على المعرف (22 حرفًا)، وحقل سلسلة (110 حرفًا)، ودقة مزدوجة، وINT.

في ElasticSearch، لا يوجد فرق واضح بين الاثنين فيما يتعلق بالفهرسة.ما زلت سأستخدم COMB_GUIDS في حالة انتقال المحتوى إلى فهارس BTREE في أي مكان في السلسلة حيث يتم تغذية المحتوى بالوقت، أو يمكن فرزه مسبقًا في حقل المعرف بحيث يكون يكون مرتبطة بالوقت ومتسلسلة جزئيًا، وسوف تتسارع.

مثيرة للاهتمام للغاية.كود Java لإنشاء COMB_GUID موجود أدناه.

import java.util.Arrays;
import java.util.UUID;
import java.util.Base64; //Only avail in Java 8+
import java.util.Date;

import java.nio.ByteBuffer; 

    private ByteBuffer babuffer = ByteBuffer.allocate( (Long.SIZE/8)*2 );
private Base64.Encoder encoder = Base64.getUrlEncoder();
public  String createId() {
    UUID uuid = java.util.UUID.randomUUID();
        return uuid2base64( uuid );
}

    public String uuid2base64(UUID uuid){ 

        Date date= new Date();
        int intFor32bits;
        synchronized(this){
        babuffer.putLong(0,uuid.getLeastSignificantBits() );
        babuffer.putLong(8,uuid.getMostSignificantBits() );

                long time=date.getTime();
        time=time >> 1; // makes it every 2 milliseconds
                intFor32bits = (int) time; // rolls over every 106 yers + 1 month from epoch
                babuffer.putInt( 0, intFor32bits);

    }
        //does this cause a memory leak?
        return encoder.encodeToString( babuffer.array() );
    }

}

وأنا messured الفرق بين ارشد (متفاوت وغير متفاوت)، متسلسل ارشد وكثافة العمليات (الهوية / autoincrement) باستخدام إطار الكيان. ومتسلسل ارشد كان من المستغرب بسرعة بالمقارنة مع كثافة مع الحفاظ على الهوية. <وأ href = "http://www.siepman.nl/blog/post/2013/10/28/ID-Sequential-Guid-COMB-Vs-Int-Identity-using-Entity-Framework.aspx" يختلط = " نوفولو "> نتائج ورمز من متسلسل ارشد هنا .

وأنا لا أرى ضرورة لمفاتيح فريدة من نوعها ليكون تخمينها أو لا، ويمر عليها من واجهة المستخدم على شبكة الإنترنت أو في أي جزء آخر يبدو عادة سيئة في حد ذاته، وأنا لا أرى، إذا كان لديك مخاوف أمنية، وكيفية استخدام ارشد يمكن أن تحسن الأمور (إذا كان هذا هو الأمر استخدام حقيقي عشوائي مولد الرقم باستخدام وظائف التشفير السليم للإطار).
وتغطي البنود الأخرى من وجهة نظري، وارشد متسلسل يمكن أن تتولد من التعليمات البرمجية دون الحاجة إلى الوصول DB (أيضا إلا إذا كان ويندوز) وانها فريدة من نوعها في الزمان والمكان.
ونعم، وطرح السؤال بقصد الإجابة عليه، لإعطاء الناس التي اخترت المعرفات الفريدة العمومية لPK لهم وسيلة لتحسين استخدام قاعدة البيانات (في حالتي سمح العملاء للحفاظ على حجم العمل أكبر بكثير دون الحاجة إلى تغيير خوادم).

ويبدو أن المخاوف الأمنية كثيرا، في هذه الحالة لا تستخدم متسلسل ارشد، أو الأفضل من ذلك، استخدم ارشد القياسي لPK التي تم تمريرها إلى الوراء وإلى الأمام من واجهة المستخدم الخاص بك وارشد متسلسل لكل شيء آخر. كما هو الحال دائما ليس هناك حقيقة مطلقة، وأنا قد قمت بتعديل الجواب أيضا الرئيسية لتعكس ذلك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top