ما هي الفهارس التي يجب استخدامها لهذا الإعداد ثنائي الجدول؟

dba.stackexchange https://dba.stackexchange.com/questions/115237

سؤال

لدي الإعداد التالي المكون من جدولين:

CREATE TABLE relationships (
    id bigint(20) NOT NULL AUTO_INCREMENT,
    type varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY id_type (id,type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE content_relations (
    relationship_id bigint(20) NOT NULL,
    site_id bigint(20) NOT NULL,
    content_id bigint(20) NOT NULL,
    PRIMARY KEY (relationship_id,site_id,content_id),
    KEY relationship (relationship_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

تم تضمين الفهارس المستخدمة حاليًا، ولكن هذا هو سؤالي بالضبط:

في ضوء الجداول المذكورة أعلاه والاستعلامات التالية، ما هي الفهارس التي يجب استخدامها (بدلاً من ذلك)؟


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

SELECT type
FROM relationships
WHERE id = X;

تكرار: عالي


DELETE
FROM relationships
WHERE id = X;

تكرار: قليل



ل ثانية الجدول فقط، يتم تنفيذ الاستعلامات التالية (كل منها مع التكرار المحدد):

SELECT site_id, content_id
FROM content_relations
WHERE relationship_id = X;

تكرار: عالي


DELETE
FROM content_relations
WHERE relationship_id = X
    AND site_id = Y;

تكرار:معتدل


DELETE
FROM content_relations
WHERE relationship_id = X;

تكرار: قليل


SELECT DISTINCT relationhip_id
FROM content_relations
WHERE site_id = X;

تكرار: منخفظ جدا



وأخيرا، هناك الاستعلام التالي باستخدام كلاهما الجداول:

SELECT r.id
FROM relationships r
INNER JOIN content_relations cr ON r.id = cr.relationship_id
WHERE cr.site_id = X
    AND cr.content_id = Y
    AND r.type = Z
LIMIT 1;

تكرار: عالي جدا



الجدول الأول (أي relationships) يحتوي على عمود يتم زيادته تلقائيًا (وبالتالي فريدًا). id, ، إذن هناك PRIMARY KEY (id).أعتقد أن هذا جيد.;)
يتضمن كلا الاستعلامين اللذين يستخدمان هذا الجدول فقط عمود المفتاح الأساسي فقط، وهذا أمر جيد جدًا بالفعل.
ثم هناك الاستعلام باستخدام كلا الجدولين، والذي يتضمن أيضًا type عمود.هل يعقل استخدام المركب الفريد KEY (id,type) هنا؟أم أنها زائدة عن الحاجة، أو على الأقل ليست ذات فائدة كبيرة؟يرجى أن تضع في اعتبارك أن هناك عددًا قليلاً من القيم المختلفة لـ type عمود.

الجدول الثاني (أي content_relations) حاليا لديه PRIMARY KEY يتكون من المجموعة (الفريدة) من الأعمدة الثلاثة.يتم استخدام هذا المفتاح للاستعلام الأخير باستخدام كلا الجدولين، ويعمل أيضًا كقيد، لأنه لكل علاقة (_id) وsite(_id) يمكن أن يكون هناك عنصر محتوى واحد فقط (أو لا يوجد) بمعرف محدد.
هناك نوعان من الاستعلامات باستخدام فقط relationship_id العمود في WHERE الشرط، ولهذا السبب هناك KEY (relationship_id).
ثم هناك استعلام واحد يتضمن أيضًا site_id عمود.وينبغي للمرء بالتالي إضافة المركب KEY (relationship_id,site_id)?
أعتقد أنه ليست هناك حاجة إلى KEY (site_id), ، والتي لن يتم استخدامها إلا من خلال استعلام واحد نادر جدًا، أليس كذلك؟

ملحوظة:ال KEY (relationship_id) هو في الأساس مفتاح خارجي لـ relationships طاولة.ومع ذلك، نظرًا لأنني أعمل في بيئة تسمح بإصدارات/محركات MySQL التي لا تتضمن قيود المفاتيح الخارجية، فلا يمكنني استخدام نسخة حقيقية من MySQL. FOREIGN KEY, ، وجميع فوائده.لكن هذا لا ينبغي أن يكون ذا صلة بسؤالي (أسئلتي)، أليس كذلك؟


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

تتعلق الجداول بالعلاقات بين عناصر المحتوى (CE).كل CE لديه معرف ونوع.لكل نوع، يوجد CE واحد فقط بمعرف محدد.ومع ذلك، قد يكون هناك CEs A و B (مع أنواع A و B)، وكلاهما لهما نفس المعرف.

يُسمح بالعلاقات بين اثنين أو أكثر من العناصر CE التي A) لها نفس النوع، وB) تنتمي إلى مواقع مختلفة (ممثلة بمعرفها الفريد).لهذا السبب type العمود جزء من relationships الجدول (بدلاً من أن يكون عمودًا في ملف content_relations طاولة).

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

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

ال AUTO_INCREMENT ل id العمود في relationships يتم استخدام الجدول، لأن هناك الكثير من العلاقات لنوع معين، وأحتاج إلى معرف فريد لكل علاقة.عند إدراج علاقة جديدة لنوع معين، يجب علي فقط توفير type, ، واحصل تلقائيًا على فريدة من نوعها id.وهذا ما AUTO_INCREMENT هو، أليس كذلك؟أم أنني أسأت فهمك يا ريك جيمس؟

أعتقد أنني لا أحتاج حقا bigint.نظرًا لأنني أشير إلى أعمدة الجداول الأخرى، وكما يتم تعريف هذه الأعمدة على أنها bigint(20), اعتقدت أنه من الجيد، إن لم يكن من الحكمة، استخدام نفس التعريف.

هناك نوعان من الاستخدامات LIMIT 1.بالنسبة للعنصر الأول، لا ينبغي أن يكون هناك أكثر من إدخال واحد (لأنني أقوم بالاستعلام عن id ومقارنة type, ، و (id,type) فريد من نوعة).لذا نعم، يمكنني/ينبغي إزالة هذا، شكرًا.ومع ذلك، في المرة الثانية، قد يكون هناك عدة إدخالات - جميعها بنفس الشيء relationship_id.لهذا السبب أتوقف بسعادة إذا حصلت على نتيجة واحدة.

هل هناك أي شيء آخر يجب أن أقدمه لك؟


هنا أ التمثيل البصري عما يدور حوله هذا كله:

visual representation

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

آمل أن يجعل هذا الأمر أكثر وضوحًا - وليس أكثر إرباكًا.

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

المحلول

تعليقات محددة

PRIMARY KEY (id),
UNIQUE KEY id_type (id,type)
) ENGINE=InnoDB

ال UNIQUE المفتاح عديم الفائدة تماما.يقوم InnoDB "بتجميع" البيانات باستخدام المفتاح الأساسي.هذا يبحث id يتيح لك العثور على بقية الأعمدة على الفور، بما في ذلك type.أيضا، بي كيه يكون مفتاح فريد، لذا فإن المفتاح الفريد لا يضيف أي قيد مفيد.

WHERE id = X

يعتني PK بذلك.(كلاهما SELECT و DELETE)

PRIMARY KEY (relationship_id,site_id,content_id),
KEY relationship (relationship_id)

وبالمثل، فإن المفتاح عديم الفائدة، قم بإسقاطه.

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

يعد PK لهذا الجدول مناسبًا لمعظم الاستعلامات.لكن...

SELECT DISTINCT relationhip_id
FROM content_relations
WHERE site_id = X;

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

INDEX(site_id, relationship_id)

INNER JOIN content_relations cr ON r.id = cr.relationship_id
WHERE cr.site_id = X
  AND cr.content_id = Y
  AND r.type = Z

إذا بدأ المحسن بـ cr,

cr: INDEX(site_id, content_id) -- in either order
r:  the existing PK will be used

سيكون الأمثل.

إذا بدأ ب r:

r:  INDEX(type, id) -- in this order  (fixed)
cr: the existing PK will be used

ملاحظات جانبية:

  • لا أرى أي سبب للإضافة id AUTO_INCREMENT إلى طاولة العلاقات
  • هل تحتاج حقا BIGINT (8 بايت)، لا يتجاوزها أحد تقريبًا INT UNSIGNED (4 بايت، بحد أقصى 4 مليار) للمعرفات.
  • لماذا LIMIT 1؟لا يهمك أي واحد؟(ربما تريد ORDER BY؟حذر:وهذا من شأنه أن يضيف التجاعيد إلى الفهارس المثالية!)
  • يكون relationships مجرد تعيين من معرف إلى رقم؟لا تهتم.تخلص من الطاولة، واستخدمها ببساطة type في جداول أخرى.
  • المزيد عن إنشاء الفهارس الأمثل.

هيكل فهارس InnoDB

الجدول هو BTree، بأمر من PRIMARY KEY.تحتوي العقدة الطرفية على جميع أعمدة الجدول.

الفهرس الثانوي (UNIQUE أم لا) هو BTree، مرتبة حسب المفتاح الثانوي.تحتوي العقدة الورقية على أعمدة PRIMARY KEY.وهذا يعني أنه عندما تبحث عن صف عبر مفتاح ثانوي، فإنه يقوم أولاً بالتنقل لأسفل في BTree الثانوية، ثم ينتقل إلى BTree الأساسي.

يحتوي "فهرس التغطية" على جميع الأعمدة المطلوبة للملف SELECT, ، وبالتالي ليست هناك حاجة "للوصول" إلى البيانات.

UNIQUE هو فهرس عادي (فهرس ثانوي مرتب في BTree)، بالإضافة إلى قيد التفرد.متى INSERTing, ، يتم التحقق من القيد.(هناك جوانب ثانوية أخرى UNIQUE.)

العودة إلى سؤالك حول الحاجة (أو لا) UNIQUE(id,type): PRIMARY KEY(id) يقول أن هناك BTree أمر بها فريد عمود id.إضافة UNIQUE (id, type) من شأنه أن يبني BTree آخر، يحتوي فقط id و type (الأعمدة الثانوية) ولا شيء إضافي للتعامل مع PK.أداء SELECT type FROM .. WHERE id=123 سيتم التنقيب في BTree للعثور عليه 123 ويجد type هناك مباشرة.ينطبق هذا البيان سواء كان يستخدم PK أو المفتاح الثانوي.ومن هنا أقول إن المفتاح الثانوي لا يمكن أن يبرر وجوده.

أما بالنسبة لـ "INDEX(site_id,content_id) على الرغم من communication_id..."، لاحظ أنني قلت أن المفتاح الثانوي سيحتوي على أعمدة PK.لذلك، فهو فعال INDEX(site_id, content_id, relationship_id) إن إدراج جميع الأعمدة الثلاثة بوضوح له ميزة جعل الأمر أكثر وضوحًا أن لديك فهرس "غطاء".ملحوظة:ال طلب من الأعمدة في فهرس مركب يفعل موضوع.

نصائح أخرى

الفهرس الخاص بك KEY relationship (relationship_id) عفا عليه الزمن.إنه فهرس مكرر مثل PK PRIMARY KEY (relationship_id,site_id,content_id) يبدأ بالفرز relationship_id لذا فإن الاستعلامات في هذا الحقل ستستخدم PK.

لاحظ أنني أقوم دائمًا بإنشاء حقل معرف من النوع INT (أو BIGINT للجداول الكبيرة، وتخزين السجلات على سبيل المثال) لـ PK، ثم يمكنك إضافة مفتاح فريد للحقل (الحقول) الذي تريده ولكن يجب أن يكون PK بسيطًا قدر الإمكان .إنه يسهل عمليات التحقق من الاتساق والنشر على حل المجموعة مثل Galera.

الملاحظة الثانية، يجب عليك دائمًا ضبط UNSIGNED في حقل PK الخاص بك، فلن تحتاج أبدًا إلى قيم سالبة فيها (وأكثر من ذلك لأنك تستخدم AUTO_INCREMENT) وستقوم بمضاعفة سعة نوع البيانات الخاص بك.

بالنسبة لي، بيان إنشاء الجدول سيكون:

CREATE TABLE relationships (
    id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
    type varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY id_type (id,type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE content_relations (
    id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
    relationship_id bigint(20) NOT NULL,
    site_id bigint(20) NOT NULL,
    content_id bigint(20) NOT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY (relationship_id,site_id,content_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

بعد قراءة إجابة @Maxime أولاً، ثم إجابة @Rick وتعليقاتها و كتاب الطبخ إندكس, ، وبعد أن قمت بإعادة النظر في استفساراتي وإعادة النظر فيها ومراجعتها أخيرًا، انتهى بي الأمر بالجداول والفهارس التالية:

CREATE TABLE relationships (
    id mediumint UNSIGNED NOT NULL AUTO_INCREMENT,
    type varchar(20) NOT NULL,
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE content_relations (
    relationship_id mediumint UNSIGNED NOT NULL,
    site_id bigint(20) UNSIGNED NOT NULL,
    content_id bigint(20) UNSIGNED NOT NULL,
    PRIMARY KEY (relationship_id,site_id,content_id),
    KEY site_content (site_id,content_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

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

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