هل هناك فرق حقيقي في الأداء بين مفاتيح INT وVARCHAR الأساسية؟

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

سؤال

هل هناك فرق في الأداء يمكن قياسه بين استخدام INT مقابل استخدام INT؟VARCHAR كمفتاح أساسي في MySQL؟أرغب في استخدام VARCHAR كمفتاح أساسي لقوائم المراجع (فكر في الولايات المتحدة ورموز البلدان) ولن يتزحزح زميل العمل عن INT AUTO_INCREMENT كمفتاح أساسي لجميع الجداول.

حجتي، على النحو المفصل هنا, ، هو أن فرق الأداء بين INT وVARCHAR لا يكاد يذكر، نظرًا لأن كل مرجع مفتاح خارجي INT سيتطلب JOIN لفهم المرجع، وسيقدم مفتاح VARCHAR المعلومات مباشرة.

لذا، هل لدى أي شخص خبرة في حالة الاستخدام هذه ومخاوف الأداء المرتبطة بها؟

نصائح أخرى

وانها ليست حول الأداء. انها حول ما يجعل مفتاح أساسي جيد. فريدة من نوعها وغير متغيرة مع مرور الوقت. قد تعتقد كيان مثل رمز البلد لا يتغير مع مرور الوقت، وسوف يكون مرشحا جيدا لمفتاح أساسي. ولكن التجربة المريرة هي ان يكون نادرا جدا.

وINT AUTO_INCREMENT يفي "فريدة من نوعها وغير متغيرة مع مرور الوقت" الشرط. ومن هنا جاء تفضيل.

يعتمد على الطول..إذا كان varchar سيتكون من 20 حرفًا، وكان int هو 4، فإذا كنت تستخدم int، فسيحتوي الفهرس الخاص بك على خمسة أضعاف عدد العقد لكل صفحة من مساحة الفهرس على القرص...وهذا يعني أن اجتياز الفهرس سيتطلب خمس عدد القراءات المادية و/أو المنطقية.

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

في نفس الوقت, ، لضمان اتساق البيانات، يجب أن يكون كل جدول مهمًا أيضًا تحتوي على مفتاح بديل غير رقمي ذي معنى، (أو فهرس فريد) لضمان عدم إمكانية إدراج صفوف مكررة (مكررة بناءً على سمات جدول ذات معنى).

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

وقطعا لا.

ولقد فعلت عدة ... عدة ... الشيكات الأداء بين INT، VARCHAR، وCHAR.

وكان 10 ملايين الجدول سجل مع مفتاح أساسي (فريد وتتجمع) بنفس السرعة الدقيق والأداء (وتكلفة الشجرة الفرعية) بغض النظر عن أي من الثلاثة استعملتها.

وأن يقال ... استخدام كل ما هو أفضل للتطبيق الخاص بك. لا تقلق بشأن الأداء.

لقد كنت منزعجًا بعض الشيء بسبب عدم وجود معايير لهذا عبر الإنترنت، لذلك أجريت اختبارًا بنفسي.

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

وكان الإعداد على النحو التالي:

  • وحدة المعالجة المركزية Intel® Core™ i7-7500U بسرعة 2.70 جيجا هرتز × 4
  • 15.6 غيغابايت من ذاكرة الوصول العشوائي، والتي تأكدت من توفر حوالي 8 غيغابايت منها أثناء الاختبار.
  • محرك أقراص SSD بسعة 148.6 جيجابايت، مع مساحة خالية كبيرة.
  • أوبونتو 16.04 64 بت
  • MySQL الإصدار 14.14 Distrib 5.7.20 لنظام التشغيل Linux (x86_64)

الطاولات:

create table jan_int (data1 varchar(255), data2 int(10), myindex tinyint(4)) ENGINE=InnoDB;
create table jan_int_index (data1 varchar(255), data2 int(10), myindex tinyint(4), INDEX (myindex)) ENGINE=InnoDB;
create table jan_char (data1 varchar(255), data2 int(10), myindex char(6)) ENGINE=InnoDB;
create table jan_char_index (data1 varchar(255), data2 int(10), myindex char(6), INDEX (myindex)) ENGINE=InnoDB;
create table jan_varchar (data1 varchar(255), data2 int(10), myindex varchar(63)) ENGINE=InnoDB;
create table jan_varchar_index (data1 varchar(255), data2 int(10), myindex varchar(63), INDEX (myindex)) ENGINE=InnoDB;

بعد ذلك، قمت بملء 10 ملايين صف في كل جدول باستخدام برنامج PHP النصي الذي يكون جوهره كما يلي:

$pdo = get_pdo();

$keys = [ 'alabam', 'massac', 'newyor', 'newham', 'delawa', 'califo', 'nevada', 'texas_', 'florid', 'ohio__' ];

for ($k = 0; $k < 10; $k++) {
    for ($j = 0; $j < 1000; $j++) {
        $val = '';
        for ($i = 0; $i < 1000; $i++) {
            $val .= '("' . generate_random_string() . '", ' . rand (0, 10000) . ', "' . ($keys[rand(0, 9)]) . '"),';
        }
        $val = rtrim($val, ',');
        $pdo->query('INSERT INTO jan_char VALUES ' . $val);
    }
    echo "\n" . ($k + 1) . ' millon(s) rows inserted.';
}

ل int الجداول، قليلا ($keys[rand(0, 9)]) تم استبداله بـ فقط rand(0, 9), ، ولل varchar الجداول، استخدمت أسماء الولايات الأمريكية كاملة، دون قصها أو تمديدها إلى 6 أحرف. generate_random_string() ينشئ سلسلة عشوائية مكونة من 10 أحرف.

ثم ركضت في MySQL:

  • SET SESSION query_cache_type=0;
  • ل jan_int طاولة:
    • SELECT count(*) FROM jan_int WHERE myindex = 5;
    • SELECT BENCHMARK(1000000000, (SELECT count(*) FROM jan_int WHERE myindex = 5));
  • بالنسبة للجداول الأخرى، نفس ما ورد أعلاه، مع myindex = 'califo' ل char الجداول و myindex = 'california' ل varchar الجداول.

أوقات ال BENCHMARK الاستعلام في كل جدول:

  • جان_نت:21.30 ثانية
  • jan_int_index:18.79 ثانية
  • جان_شار:21.70 ثانية
  • jan_char_index:18.85 ثانية
  • jan_varchar:21.76 ثانية
  • jan_varchar_index:18.86 ثانية

فيما يتعلق بأحجام الجدول والفهرس، إليك مخرجات show table status from janperformancetest; (مع عدم ظهور بعض الأعمدة):

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Name              | Engine | Version | Row_format | Rows    | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Collation              |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| jan_int           | InnoDB |      10 | Dynamic    | 9739094 |             43 |   422510592 |               0 |            0 |   4194304 |           NULL | utf8mb4_unicode_520_ci |  
| jan_int_index     | InnoDB |      10 | Dynamic    | 9740329 |             43 |   420413440 |               0 |    132857856 |   7340032 |           NULL | utf8mb4_unicode_520_ci |   
| jan_char          | InnoDB |      10 | Dynamic    | 9726613 |             51 |   500170752 |               0 |            0 |   5242880 |           NULL | utf8mb4_unicode_520_ci |  
| jan_char_index    | InnoDB |      10 | Dynamic    | 9719059 |             52 |   513802240 |               0 |    202342400 |   5242880 |           NULL | utf8mb4_unicode_520_ci |  
| jan_varchar       | InnoDB |      10 | Dynamic    | 9722049 |             53 |   521142272 |               0 |            0 |   7340032 |           NULL | utf8mb4_unicode_520_ci |   
| jan_varchar_index | InnoDB |      10 | Dynamic    | 9738381 |             49 |   486539264 |               0 |    202375168 |   7340032 |           NULL | utf8mb4_unicode_520_ci | 
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

استنتاجي هو أنه لا يوجد فرق في الأداء لحالة الاستخدام المحددة هذه.

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

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

وأما بالنسبة لمفتاح أساسي، أيا كان يجعل جسديا على التوالي ينبغي تحديد كمفتاح أساسي فريدة من نوعها.

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

ووdrawaback لاستخدام بديل هو أنك يمكن ان تسمح المتغيرة للمعنى بديل:

ex.
id value
1 A
2 B
3 C

Update 3 to D
id value
1 A
2 B
3 D

Update 2 to C
id value
1 A
2 C
3 D

Update 3 to B
id value
1 A
2 C
3 B

وكل هذا يتوقف على ما تحتاجه حقا ما يدعو للقلق في الهيكل الخاص وما يعني أكثر من غيرها.

الحالات الشائعة التي يكون فيها بديل AUTO_INCREMENT يؤلم:

نمط المخطط الشائع هو رسم خرائط كثير إلى كثير:

CREATE TABLE map (
    id ... AUTO_INCREMENT,
    foo_id ...,
    bar_id ...,
    PRIMARY KEY(id),
    UNIQUE(foo_id, bar_id),
    INDEX(bar_id) );

أداء هذا النمط أفضل بكثير، خاصة عند استخدام InnoDB:

CREATE TABLE map (
    # No surrogate
    foo_id ...,
    bar_id ...,
    PRIMARY KEY(foo_id, bar_id),
    INDEX      (bar_id, foo_id) );

لماذا؟

  • تحتاج مفاتيح InnoDB الثانوية إلى بحث إضافي؛عن طريق تحريك الزوج إلى PK، يتم تجنب ذلك في اتجاه واحد.
  • الفهرس الثانوي "مغطي"، لذا فهو لا يحتاج إلى بحث إضافي.
  • هذا الجدول أصغر بسبب التخلص منه id ومؤشر واحد.

قضية أخرى (دولة):

country_id INT ...
-- versus
country_code CHAR(2) CHARACTER SET ascii

في كثير من الأحيان، يقوم المبتدئ بتطبيع رمز البلد إلى 4 بايت INT بدلاً من استخدام سلسلة 2 بايت "طبيعية" لا تتغير تقريبًا.أسرع، وأصغر، وعدد أقل من عمليات الانضمام، وأكثر قابلية للقراءة.

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

وأيضا، إذا كنت ترك الناس كتابة واجهات لقاعدة البيانات الخاصة بك، مفتاح بديل يمكن أن تكون مفيدة. يمكن لل3rd الطرف تعتمد على حقيقة أن مفتاح بديل سوف تتغير إلا في ظروف نادرة جدا.

والسؤال هو حول الخلية لذلك أنا أقول هناك فرق كبير. إذا كان عن أوراكل (الذي يخزن أرقام كسلسلة - نعم، أنا لا يمكن أن أصدق ذلك في البداية) ثم ليس هناك فرق كبير

والتخزين في الجدول ليست القضية لكن تحديث ومشيرا إلى المؤشر. استفسارات تتعلق بالبحث عن سجل استنادا المفتاح الأساسي متكررة - تريد لها أن تحدث في أسرع وقت ممكن لأنها تحدث في كثير من الأحيان

.

والشيء هو يتعامل مع وحدة المعالجة المركزية 4 بايت و 8 صحيحة بايت بشكل طبيعي، في السيليكون . انها سريعة حقا من أجل أن المقارنة بين الأعداد الصحيحة - ما يحدث في واحدة أو دورتين على مدار الساعة.

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

وبلدي القاعدة العامة - يجب أن تكون كل المفتاح الأساسي لINT autoincrementing خصوصا في OO التطبيقات باستخدام ORM (السبات، Datanucleus، أيا كان) حيث هناك الكثير من العلاقات بين الأشياء - أنها سوف عادة يكون دائما تنفيذها باعتبارها FK بسيط و القدرة لDB لحل تلك بسرعة من المهم أن التطبيق الخاص بك الصورة الاستجابة.

وأنا واجهت نفس المعضلة. لقد تقدمت DW (كوكبة مخطط) مع الجداول 3 الواقع، حوادث الطرق، المركبات في الحوادث والإصابات في الحوادث. يتضمن بيانات عن الحوادث المسجلة في المملكة المتحدة 1979-2012، و 60 الجداول البعد. كل ذلك معا، نحو 20 مليون اسطوانة.

العلاقات

حقيقة الجداول:

+----------+          +---------+
| Accident |>--------<| Vehicle |
+-----v----+ 1      * +----v----+
     1|                    |1
      |    +----------+    |
      +---<| Casualty |>---+
         * +----------+ *

وRDMS: الخلية 5.6

وأصلا مؤشر الحوادث هو VARCHAR (الأرقام والحروف)، مع 15 أرقام. حاولت ليس لديهم مفاتيح بديلة، وبمجرد أن مؤشرات حادث من شأنه أن تتغير أبدا. في كمبيوتر i7 من (8 النوى)، وأصبح DW بطيئة جدا للاستعلام بعد 12 مليون سجلات الحمل اعتمادا الأبعاد. بعد الكثير من إعادة العمل وإضافة مفاتيح بديلة BIGINT حصلت على معدل 20٪ زيادة سرعة الأداء. بعد أن كسب الأداء المنخفض، ولكن محاولة صالحة. ايم العمل في الخلية وضبط والمجموعات.

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

وكما جرت العادة، لا توجد إجابات بطانية. 'هذا يعتمد!' وأنا لست مستهزئا. كان فهمي للمسألة الأصلي للمفاتيح على الجداول الصغيرة - مثل البلد (عدد صحيح معرف أو رمز شار / VARCHAR) كونه المفتاح الخارجي إلى جدول المحتمل أن يكون كبيرا مثل جدول عنوان / اتصال

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

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

وورموز صغيرة الحجم - وليس أكثر من 3 أحرف عادة للبلد والدولة، قد يكون من المقبول استخدام مفاتيح الطبيعية ومفاتيح خارجية في هذا السيناريو

.

والسيناريو الآخر حيث تعتمد على القيم VARCHAR أطول وربما على الجداول أكبر مفاتيح. مفتاح بديل ربما لديه ميزة.

اسمحوا لي أن أقول نعم بالتأكيد هناك فرق، مع الأخذ بعين الاعتبار نطاق الأداء (التعريف خارج الصندوق):

1- يعد استخدام int البديل أسرع في التطبيق لأنك لا تحتاج إلى استخدام ToUpper() أو ToLower() أو ToUpperInvarient() أو ToLowerInvarient() في التعليمات البرمجية الخاصة بك أو في استعلامك وهذه الوظائف الأربع لها معايير أداء مختلفة.راجع قواعد أداء Microsoft في هذا الشأن.(أداء التطبيق)

2- استخدام بديل int يضمن عدم تغيير المفتاح مع مرور الوقت.حتى رموز البلدان قد تتغير، راجع ويكيبيديا كيف تغيرت رموز ISO بمرور الوقت.قد يستغرق ذلك الكثير من الوقت لتغيير المفتاح الأساسي للأشجار الفرعية.(أداء صيانة البيانات)

3- يبدو أن هناك مشكلات في حلول ORM، مثل NHibernate عندما لا يكون PK/FK صحيحًا.(أداء المطور)

scroll top