لا يستخدم الاستعلام مؤشرًا تغطية عند الاقتضاء
-
03-10-2019 - |
سؤال
لقد قمت بتنزيل قاعدة بيانات الموظفين ونفذ بعض الاستفسارات لأغراض القياس.
ثم لاحظت أن استعلام واحد لم يستخدم فهرس تغطية ، على الرغم من وجود فهرس مقابل قمت بإنشائه مسبقًا. فقط عندما أضفت أ FORCE INDEX
بند إلى الاستعلام ، استخدم فهرس تغطية.
لقد قمت بتحميل ملفين ، واحد هو استفسارات SQL المنفذة والآخر النتائج.
هل يمكنك معرفة سبب استخدام الاستعلام إلى مؤشر تغطية فقط عندما أ FORCE INDEX
يضاف البند؟ يوضح التوضيح أنه في كلتا الحالتين ، الفهرس dept_no_from_date_idx
يتم استخدامه على أي حال.
للتكيف مع معايير ذلك ، أكتب أيضًا محتوى الملف هنا:
استعلامات SQL:
USE employees;
/* Creating an index for an index-covered query */
CREATE INDEX dept_no_from_date_idx ON dept_emp (dept_no, from_date);
/* Show `dept_emp` table structure, indexes and generic data */
SHOW TABLE STATUS LIKE "dept_emp";
DESCRIBE dept_emp;
SHOW KEYS IN dept_emp;
/* The EXPLAIN shows that the subquery doesn't use a covering-index */
EXPLAIN SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
/* The subquery should use a covering index, but isn't */
SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50
) AS `der` USING (`emp_no`, `dept_no`);
/* The EXPLAIN shows that the subquery DOES use a covering-index,
thanks to the FORCE INDEX clause */
EXPLAIN SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
/* The subquery use a covering index */
SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp FORCE INDEX(dept_no_from_date_idx) WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50
) AS `der` USING (`emp_no`, `dept_no`);
النتائج:
--------------
/* Creating an index for an index-covered query */
CREATE INDEX dept_no_from_date_idx ON dept_emp (dept_no, from_date)
--------------
Query OK, 331603 rows affected (33.95 sec)
Records: 331603 Duplicates: 0 Warnings: 0
--------------
/* Show `dept_emp` table structure, indexes and generic data */
SHOW TABLE STATUS LIKE "dept_emp"
--------------
+----------+--------+---------+------------+--------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------+
| Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Create_time | Update_time | Check_time | Collation | Checksum | Create_options | Comment |
+----------+--------+---------+------------+--------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------+
| dept_emp | InnoDB | 10 | Compact | 331883 | 36 | 12075008 | 0 | 21544960 | 29360128 | NULL | 2010-05-04 13:07:49 | NULL | NULL | utf8_general_ci | NULL | | |
+----------+--------+---------+------------+--------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------+
1 row in set (0.47 sec)
--------------
DESCRIBE dept_emp
--------------
+-----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| dept_no | char(4) | NO | PRI | NULL | |
| from_date | date | NO | | NULL | |
| to_date | date | NO | | NULL | |
+-----------+---------+------+-----+---------+-------+
4 rows in set (0.05 sec)
--------------
SHOW KEYS IN dept_emp
--------------
+----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| dept_emp | 0 | PRIMARY | 1 | emp_no | A | 331883 | NULL | NULL | | BTREE | |
| dept_emp | 0 | PRIMARY | 2 | dept_no | A | 331883 | NULL | NULL | | BTREE | |
| dept_emp | 1 | emp_no | 1 | emp_no | A | 331883 | NULL | NULL | | BTREE | |
| dept_emp | 1 | dept_no | 1 | dept_no | A | 7 | NULL | NULL | | BTREE | |
| dept_emp | 1 | dept_no_from_date_idx | 1 | dept_no | A | 13 | NULL | NULL | | BTREE | |
| dept_emp | 1 | dept_no_from_date_idx | 2 | from_date | A | 165941 | NULL | NULL | | BTREE | |
+----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
6 rows in set (0.23 sec)
--------------
/* The EXPLAIN shows that the subquery doesn't use a covering-index */
EXPLAIN SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
/* The subquery should use a covering index, but isn't */
SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50
) AS `der` USING (`emp_no`, `dept_no`)
--------------
+----+-------------+------------+--------+----------------------------------------------+-----------------------+---------+------------------------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+----------------------------------------------+-----------------------+---------+------------------------+-------+-------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 50 | |
| 1 | PRIMARY | dept_emp | eq_ref | PRIMARY,emp_no,dept_no,dept_no_from_date_idx | PRIMARY | 16 | der.emp_no,der.dept_no | 1 | |
| 2 | DERIVED | dept_emp | ref | dept_no,dept_no_from_date_idx | dept_no_from_date_idx | 12 | | 21402 | Using where |
+----+-------------+------------+--------+----------------------------------------------+-----------------------+---------+------------------------+-------+-------------+
3 rows in set (0.09 sec)
--------------
/* The EXPLAIN shows that the subquery DOES use a covering-index,
thanks to the FORCE INDEX clause */
EXPLAIN SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
/* The subquery use a covering index */
SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp FORCE INDEX(dept_no_from_date_idx) WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50
) AS `der` USING (`emp_no`, `dept_no`)
--------------
+----+-------------+------------+--------+----------------------------------------------+-----------------------+---------+------------------------+-------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+----------------------------------------------+-----------------------+---------+------------------------+-------+--------------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 50 | |
| 1 | PRIMARY | dept_emp | eq_ref | PRIMARY,emp_no,dept_no,dept_no_from_date_idx | PRIMARY | 16 | der.emp_no,der.dept_no | 1 | |
| 2 | DERIVED | dept_emp | ref | dept_no_from_date_idx | dept_no_from_date_idx | 12 | | 37468 | Using where; Using index |
+----+-------------+------------+--------+----------------------------------------------+-----------------------+---------+------------------------+-------+--------------------------+
3 rows in set (0.05 sec)
Bye
يحرر:
لقد لاحظت أن هناك اختلافات كبيرة في سرعة التنفيذ بين هذا الاستعلامات الأخيرة ، يتم وضع النتائج أمامك:
SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50
) AS `der` USING (`emp_no`, `dept_no`)
--------------
+--------+---------+------------+------------+
| emp_no | dept_no | from_date | to_date |
+--------+---------+------------+------------+
| 38552 | d001 | 1985-04-16 | 2000-10-20 |
... omitted ...
| 98045 | d001 | 1985-03-28 | 9999-01-01 |
+--------+---------+------------+------------+
50 rows in set (0.31 sec)
--------------
SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp FORCE INDEX(dept_no_from_date_idx) WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50
) AS `der` USING (`emp_no`, `dept_no`)
--------------
+--------+---------+------------+------------+
| emp_no | dept_no | from_date | to_date |
+--------+---------+------------+------------+
| 38552 | d001 | 1985-04-16 | 2000-10-20 |
... omitted ...
| 98045 | d001 | 1985-03-28 | 9999-01-01 |
+--------+---------+------------+------------+
50 rows in set (0.06 sec)
ولكن ، إذا قمت بتغيير ترتيب التنفيذ (جعل الاستعلام الأخير يتم تنفيذه أولاً ، وأول استعلام يتم تنفيذه أخيرًا) ، فإن سرعة التنفيذ هي نفسها:
--------------
SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp FORCE INDEX(dept_no_from_date_idx) WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50
) AS `der` USING (`emp_no`, `dept_no`)
--------------
+--------+---------+------------+------------+
| emp_no | dept_no | from_date | to_date |
+--------+---------+------------+------------+
| 38552 | d001 | 1985-04-16 | 2000-10-20 |
... omitted ...
| 98045 | d001 | 1985-03-28 | 9999-01-01 |
+--------+---------+------------+------------+
50 rows in set (0.08 sec)
--------------
SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
SELECT SQL_NO_CACHE emp_no, dept_no FROM dept_emp WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50
) AS `der` USING (`emp_no`, `dept_no`)
--------------
+--------+---------+------------+------------+
| emp_no | dept_no | from_date | to_date |
+--------+---------+------------+------------+
| 38552 | d001 | 1985-04-16 | 2000-10-20 |
... omitted ...
| 98045 | d001 | 1985-03-28 | 9999-01-01 |
+--------+---------+------------+------------+
50 rows in set (0.08 sec)
لا يمكن أن يكون الاستعلام الثاني مأخوذ من ذاكرة التخزين المؤقت ، لأن SQL_NO_Cache مكتوب في كلا الاستعلامات. فلماذا في المثال الأول استغرق الاستعلام الأول 0.31 ثانية والثاني 0.06 ثانية ، ولكن في المثال الثاني ، يستغرق كلا الاستعلامات 0.08 ثانية؟
EDIT2:
أعتقد أن اختلافات سرعة التنفيذ مستمدة من ذاكرة التخزين المؤقت OS وربما عوامل أخرى. عند تنفيذ الاستعلامات 2 أعلاه مرارًا وتكرارًا ، تصبح اختلافات وقت التنفيذ ضئيلة. لقد نفذت الاستعلامات 2 أعلاه لمدة 3 مرات مرارًا وتكرارًا وحصلت على النتائج التالية:
#1: 0.08 sec
#2: 0.03 sec
#1: 0.05 sec
#2: 0.05 sec
#1: 0.03 sec
#2: 0.05 sec
المحلول
في الواقع ، تستخدم كل من استفساراتك فهرس التغطية.
لا يتضمن تعريف الفهرس الخاص بك emp_no
, ، في ذلك MyISAM
, Using index
سيكون من المستحيل حتى مع FORCE INDEX
بند.
لكن، InnoDB
يتم تجميع الجداول ويحتوي كل فهرس ضمنيًا على PRIMARY KEY
كمؤشر تسجيل.
هذا يعني أن الفهرس الخاص بك هو في الواقع فهرس على (dept_no, from_date, emp_no, dept_no)
وبالتالي يحتوي على جميع الحقول المطلوبة.
EXPLAIN PLAN
لا يعكس هذا دائمًا بشكل صحيح ، ولكن InnoDB
المحرك يتعامل مع هذا.
يمكنك التحقق من ذلك من خلال مقارنة أداء هذين الاستفسارات:
SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
/* The subquery use a covering index */
SELECT SQL_NO_CACHE from_date, emp_no, dept_no FROM dept_emp WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50
) AS `der` USING (`emp_no`, `dept_no`);
و
SELECT SQL_NO_CACHE * FROM dept_emp INNER JOIN (
/* The subquery use a covering index */
SELECT SQL_NO_CACHE to_date, emp_no, dept_no FROM dept_emp WHERE dept_no="d001" ORDER BY from_date DESC LIMIT 20000,50
) AS `der` USING (`emp_no`, `dept_no`);
سترى أنه على الرغم من أن الخطط ستظهر على أنها متطابقة ، فإن الاستعلام الثاني سيستغرق المزيد من الوقت (تمامًا بسبب ذلك تمامًا بسبب to_date
غير مغطى).
إنه خطأ في EXPLAIN PLAN
, ، ليس في InnoDB
محرك.