it-swarm.asia

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

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

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

لذلك ، هل لدى أي شخص تجربة مع حالة الاستخدام المعينة هذه وشواغل الأداء المرتبطة بها؟

151
Jake McGraw

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

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

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

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

70
Bill Karwin

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

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

77
Steve McLeod

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

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

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

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

34
Charles Bretana

بالطبع لا.

لقد فعلت عدة ... عدة ... اختبارات الأداء بين INT و VARCHAR و CHAR.

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

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

31
Timothy Khouri

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

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

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

  • Intel® Core ™ i7-7500U CPU @ 2.70GHz × 4
  • 15.6 GiB من ذاكرة الوصول العشوائي ، والتي ضمنت حوالي 8 غيغابايت كانت حرة خلال الاختبار.
  • محرك أقراص SSD سعة 148.6 جيجا بايت ، مع الكثير من المساحة الحرة.
  • أوبونتو 16.04 64 بت
  • MySQL Ver 14.14 Distrib 5.7.20، for 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 في كل جدول:

  • jan_int: 21.30 ثانية
  • jan_int_index: 18.79 ثانية
  • jan_char: 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 | 
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

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

25
Jan Żankowski

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

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

9
Joel Coehoorn

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

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

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

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

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

6
LeppyR64

الحالات الشائعة التي يضر فيها 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

غالبًا ما يقوم المبتدئ بتطبيع كود country_code في INT من 4 بايت بدلاً من استخدام سلسلة ثنائية البايت "طبيعية" ، لا تتغير تقريبًا. أسرع ، أصغر ، عدد أقل من JOINs ، أكثر قابلية للقراءة.

2
Rick James

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

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

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

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

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

2
Volksman

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

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

2
Herman J. Radtke III

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

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

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

RDMS: MySQL 5.6

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

1
Diego Duarte

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

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

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

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

0
Shadi Namrouti

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

0
George Jempty

كالعادة ، لا توجد إجابات شاملة. 'هذا يعتمد!' وأنا لا يجري طريف. كانت فهمي للسؤال الأصلي تتعلق بالمفاتيح في الجداول الصغيرة - مثل البلد (الرقم الصحيح أو كود char/varchar) باعتباره مفتاحًا خارجيًا لجدول يحتمل أن يكون ضخمًا مثل جدول العناوين/جهة الاتصال.

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

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

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

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

0
Vinod