เหตุใดจึงเลือกอย่างง่ายบน InnoDB 100x ช้ากว่าบน MyISAM


33

ฉันมีปัญหาค่อนข้างน่ารำคาญ ฉันต้องการใช้ INNODB เป็นเอ็นจิ้นฐานข้อมูลหลักของฉันและยอมแพ้กับ MyISAM เพราะฉันต้องการรุ่นเก่าสำหรับการใช้ galera-cluster เพื่อความซ้ำซ้อน

ฉันคัดลอก (คำอธิบายด้านล่าง) newbb_postตารางเป็นตารางใหม่ที่เรียกว่าnewbb_innopostและเปลี่ยนเป็น InnoDB ขณะนี้ตารางเก็บ5,390,146ข้อมูลแต่ละรายการ

การรันตัวเลือกเหล่านี้บนฐานข้อมูลที่เพิ่งเริ่มต้นใหม่ (ดังนั้นจึงไม่มีการแคชใด ๆ เกี่ยวข้องในจุดนี้!) ฐานข้อมูลให้ผลลัพธ์ต่อไปนี้ (ไม่แสดงผลลัพธ์ทั้งหมดโปรดทราบว่าฉันไม่ได้ขอให้ฐานข้อมูลเรียงลำดับผลลัพธ์):

เลือก post.postid, post.attach จาก newbb_post เป็นโพสต์ WHERE post.threadid = 51506;

.
.
| 5401593 | 0 |
| 5401634 | 0 |
+ --------- + -------- +
62510 แถวในชุด (0.13 วินาที)
เลือก post.postid, post.attach จาก newbb_innopost เป็นโพสต์ WHERE post.threadid = 51506;
.
.
| 5397410 | 0 |
| 5397883 | 0 |
+ --------- + -------- +
62510 แถวในชุด (1 นาที 22.19 วินาที)

0.13 วินาทีถึง 86.19 วินาที (!)

ฉันสงสัยว่าทำไมสิ่งนี้จึงเกิดขึ้น ฉันได้อ่านคำตอบบางส่วนที่นี่ใน Stackexchange ที่เกี่ยวข้องกับ InnoDB และบางคนแนะนำให้เพิ่มinnodb_buffer_poolขนาดเป็น 80% ของ RAM ที่ติดตั้ง วิธีนี้จะไม่ช่วยแก้ปัญหาได้ซึ่งการสอบถามเริ่มต้นไปยัง ID ที่เจาะจงจะใช้เวลาอย่างน้อย 50x และหยุดการทำงานของ websever ทั้งหมดรอคิวการเชื่อมต่อและการสืบค้นสำหรับฐานข้อมูล หลังจากนั้นแคช / บัฟเฟอร์อาจเริ่มทำงาน แต่มีมากกว่า 100,000 เธรดในฐานข้อมูลนี้ดังนั้นจึงเป็นไปได้มากที่แคชจะไม่เก็บคิวรีที่เกี่ยวข้องทั้งหมดที่จะให้บริการ

แบบสอบถามข้างต้นนั้นง่าย (ไม่รวม) และใช้คีย์ทั้งหมด:

อธิบายการเลือก post.postid, post.attach จาก newbb_innopost เป็นโพสต์ WHERE post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + + ---------- ---- ----- + ------- + -------- + ------- +
| id | select_type | ตาราง | ประเภท | possible_keys | กุญแจ key_len | ref | แถว | พิเศษ |
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + + ---------- ---- ----- + ------- + -------- + ------- +
| 1 | ง่าย ๆ โพสต์ | ref | threadid, threadid_2, threadid_visible_dateline | เธรด 4 | const | 120144 | |
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + + ---------- ---- ----- + ------- + -------- + ------- +

นี่คือ MyISAM-Table:

สร้างตาราง `newbb_post` (
  `postid` int (10) ไม่ได้ลงนามไม่ใช่ NULL AUTO_INCREMENT
  `threadid` int (10) ไม่ได้ลงนามไม่เป็นโมฆะค่าเริ่มต้น '0'
  `parentid` int (10) ไม่ได้ลงนามไม่เป็นโมฆะค่าเริ่มต้น '0'
  `ชื่อผู้ใช้ 'varchar (100) ไม่ใช่ค่าเริ่มต้นที่เป็นศูนย์' '
  `userid` int (10) ไม่ได้ลงนามไม่เป็นโมฆะค่าเริ่มต้น '0'
  `title` varchar (250) ไม่ใช่ค่าเริ่มต้นที่เป็นศูนย์ ''
  `dateline` int (10) ไม่ได้ลงนามไม่เป็นศูนย์ค่าเริ่มต้น '0'
  `pagetext` mediumtext
  `allowmilie` smallint (6) ไม่เป็นค่าเริ่มต้น '0'
  `showsignature` smallint (6) ไม่เป็นค่าศูนย์ '0'
  `ipaddress` varchar (15) ไม่เป็นค่าเริ่มต้น ''
  `iconid` smallint (5) unsigned NOT NULL DEFAULT '0',
  `visible` smallint (6) ไม่เป็นค่าเริ่มต้น '0'
  `attach` smallint (5) unsigned NOT NFA DEFAULT '0',
  `infraction` smallint (5) unsigned NOT NFA DEFAULT '0',
  `int รายงาน (int) (10) ไม่ได้ลงนามไม่เป็นศูนย์ค่าเริ่มต้น '0'
  `importthreadid` bigint (20) ไม่ใช่ NULL DEFAULT '0',
  `importpostid` bigint (20) ไม่เป็นค่าเริ่มต้น '0'
  `conversion_2_utf8` int (11) ไม่เป็นโมฆะ
  `htmlstate` enum ('ปิด', 'บน', 'on_nl2br') ไม่ได้ตั้งค่าเริ่มต้นเป็นโมฆะ 'on_nl2br'
  คีย์หลัก (`postid`)
  KEY `threadid` (` threadid`, `userid`)
  KEY `importpost_index` (` importpostid`)
  KEY `dateline '(` dateline`)
  KEY `threadid_2` (` threadid ', `visible',` dateline`)
  KEY `convert_2_utf8` (` conversion_2_utf8`)
  KEY `threadid_visible_dateline` (` threadid ', `visible',` dateline ', `userid`,` postid`),
  KEY `ipaddress` (` ipaddress`)
  KEY `userid` (` userid`, `parentid`)
  KEY `user_date` (` userid`, `dateline`)
) ENGINE = MyISAM AUTO_INCREMENT = 5402802 ค่าเริ่มต้น = ละติน 1

และนี่คือตาราง InnoDB (เหมือนกันทุกประการ):

สร้างตาราง `newbb_innopost` (
  `postid` int (10) ไม่ได้ลงนามไม่ใช่ NULL AUTO_INCREMENT
  `threadid` int (10) ไม่ได้ลงนามไม่เป็นโมฆะค่าเริ่มต้น '0'
  `parentid` int (10) ไม่ได้ลงนามไม่เป็นโมฆะค่าเริ่มต้น '0'
  `ชื่อผู้ใช้ 'varchar (100) ไม่ใช่ค่าเริ่มต้นที่เป็นศูนย์' '
  `userid` int (10) ไม่ได้ลงนามไม่เป็นโมฆะค่าเริ่มต้น '0'
  `title` varchar (250) ไม่ใช่ค่าเริ่มต้นที่เป็นศูนย์ ''
  `dateline` int (10) ไม่ได้ลงนามไม่เป็นศูนย์ค่าเริ่มต้น '0'
  `pagetext` mediumtext
  `allowmilie` smallint (6) ไม่เป็นค่าเริ่มต้น '0'
  `showsignature` smallint (6) ไม่เป็นค่าศูนย์ '0'
  `ipaddress` varchar (15) ไม่เป็นค่าเริ่มต้น ''
  `iconid` smallint (5) unsigned NOT NULL DEFAULT '0',
  `visible` smallint (6) ไม่เป็นค่าเริ่มต้น '0'
  `attach` smallint (5) unsigned NOT NFA DEFAULT '0',
  `infraction` smallint (5) unsigned NOT NFA DEFAULT '0',
  `int รายงาน (int) (10) ไม่ได้ลงนามไม่เป็นศูนย์ค่าเริ่มต้น '0'
  `importthreadid` bigint (20) ไม่ใช่ NULL DEFAULT '0',
  `importpostid` bigint (20) ไม่เป็นค่าเริ่มต้น '0'
  `conversion_2_utf8` int (11) ไม่เป็นโมฆะ
  `htmlstate` enum ('ปิด', 'บน', 'on_nl2br') ไม่ได้ตั้งค่าเริ่มต้นเป็นโมฆะ 'on_nl2br'
  คีย์หลัก (`postid`)
  KEY `threadid` (` threadid`, `userid`)
  KEY `importpost_index` (` importpostid`)
  KEY `dateline '(` dateline`)
  KEY `threadid_2` (` threadid ', `visible',` dateline`)
  KEY `convert_2_utf8` (` conversion_2_utf8`)
  KEY `threadid_visible_dateline` (` threadid ', `visible',` dateline ', `userid`,` postid`),
  KEY `ipaddress` (` ipaddress`)
  KEY `userid` (` userid`, `parentid`)
  KEY `user_date` (` userid`, `dateline`)
) ENGINE = InnoDB AUTO_INCREMENT = 5402802 ค่าเริ่มต้น = ละติน 1

เซิร์ฟเวอร์พร้อม RAM 32GB:

รุ่นเซิร์ฟเวอร์: 10.0.12-MariaDB-1 ~ trusty-wsrep-log mariadb.org การแจกแจงแบบไบนารี, wsrep_25.10.r4002

หากคุณต้องการการตั้งค่าตัวแปร innodb_ ทั้งหมดฉันสามารถแนบไฟล์นั้นกับโพสต์นี้ได้

ปรับปรุง:

ฉันลบดัชนีทั้งหมดนอกเหนือจากดัชนีหลักหลังจากนั้นผลลัพธ์จะเป็นดังนี้:

.
.
| 5402697 | 0 |
| 5402759 | 0 |
+ --------- + -------- +
62510 แถวในชุด (29.74 วินาที)
อธิบายการเลือก post.postid, post.attach จาก newbb_innopost เป็นโพสต์ WHERE post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
| id | select_type | ตาราง | ประเภท | possible_keys | กุญแจ key_len | ref | แถว | พิเศษ |
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
| 1 | ง่าย ๆ โพสต์ | ทั้งหมด | NULL | NULL | NULL | NULL | 5909836 | ใช้ที่ไหน |
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
1 แถวในชุด (0.00 วินาที)

หลังจากนี้ฉันเพิ่งเพิ่มหนึ่งดัชนีกลับไปที่การผสม, threadid, ผลลัพธ์มีดังนี้:

.
.
| 5402697 | 0 |
| 5402759 | 0 |
+ --------- + -------- +
62510 แถวในชุด (11.58 วินาที)
อธิบายการเลือก post.postid, post.attach จาก newbb_innopost เป็นโพสต์ WHERE post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
| id | select_type | ตาราง | ประเภท | possible_keys | กุญแจ key_len | ref | แถว | พิเศษ |
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
| 1 | ง่าย ๆ โพสต์ | ref | เธรด เธรด 4 | const | 124622 | |
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
1 แถวในชุด (0.00 วินาที)

แปลกที่ไม่มีดัชนีที่เกี่ยวข้องใด ๆ การสแกนแบบเต็มใช้เวลาเพียง 29 วินาทีเมื่อเทียบกับ 88 วินาทีโดยใช้ดัชนี (!)

ด้วยดัชนีที่ปรับแต่งได้อย่างสมบูรณ์แบบเพียงหนึ่งเดียวก็ยังคงใช้เวลา 11 วินาทีในการทำให้สมบูรณ์ - ยังช้าเกินไปสำหรับการใช้งานในโลกแห่งความเป็นจริง

อัปเดต 2:

ฉันติดตั้ง MySQL (5.5.38-0ubuntu0.14.04.1 (Ubuntu)) บนเซิร์ฟเวอร์อื่นที่มีการกำหนดค่าฮาร์ดแวร์ที่แน่นอนและฐานข้อมูล / ตารางเดียวกัน

ผลลัพธ์ใกล้เคียงกันเป็นอันดับแรกตาราง MyISAM:

.
.
| 5401593 | 0 |
| 5401634 | 0 |
+ --------- + -------- +
62510 แถวในชุด (0.14 วินาที)

และนี่คือผลลัพธ์ของตาราง InnoDB

.
.
| 5397410 | 0 |
| 5397883 | 0 |
+ --------- + -------- +
62510 แถวในชุด (1 นาที 17.63 วินาที)

ปรับปรุง 3: เนื้อหาของ my.cnf

# ไฟล์การกำหนดค่าเซิร์ฟเวอร์ฐานข้อมูล MariaDB
#
# คุณสามารถคัดลอกไฟล์นี้ไปยังหนึ่งใน:
# - "/etc/mysql/my.cnf" เพื่อตั้งค่าตัวเลือกสากล
# - "~ / .my.cnf" เพื่อตั้งค่าตัวเลือกเฉพาะผู้ใช้
# 
# หนึ่งสามารถใช้ตัวเลือกแบบยาวทั้งหมดที่โปรแกรมรองรับ
# เรียกใช้โปรแกรมด้วย - ช่วยเพื่อรับรายการตัวเลือกที่มีและด้วย
# - พิมพ์ค่าเริ่มต้นเพื่อดูว่าจริง ๆ แล้วมันจะเข้าใจและใช้
#
# สำหรับคำอธิบายดู
# http://dev.mysql.com/doc/mysql/th/server-system-variables.html

# สิ่งนี้จะถูกส่งไปยังไคลเอนต์ mysql ทั้งหมด
# มีรายงานว่ารหัสผ่านควรอยู่ในเครื่องหมายคำพูด / คำพูด
# escpecially หากมีอักขระ "#" ...
# อย่าลืมแก้ไข /etc/mysql/debian.cnf เมื่อเปลี่ยนตำแหน่งซ็อกเก็ต
[ลูกค้า]
พอร์ต = 3306
socket = /var/run/mysqld/mysqld.sock

# นี่คือรายการสำหรับบางโปรแกรมเฉพาะ
# ค่าต่อไปนี้สมมติว่าคุณมี RAM อย่างน้อย 32M

# นี่เป็นที่รู้จักอย่างเป็นทางการในชื่อ [safe_mysqld] ทั้งสองเวอร์ชันแยกวิเคราะห์อยู่ในขณะนี้
[mysqld_safe]
socket = /var/run/mysqld/mysqld.sock
ดี = 0

[mysqld]
#
# * การตั้งค่าพื้นฐาน
#
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
พอร์ต = 3306
basedir = / usr
datadir = / var / lib / mysql
tmpdir = / tmp
lc_messages_dir = / usr / share / mysql
lc_messages = en_US
ข้ามภายนอกล็อค
#
# แทนที่จะข้ามเครือข่ายตอนนี้ค่าเริ่มต้นจะรับฟังได้เท่านั้น
# localhost ซึ่งเข้ากันได้มากกว่าและไม่ปลอดภัยน้อยกว่า
bind-address = 127.0.0.1
#
# * Fine Tuning
#
max_connections = 100
connect_timeout = 5
wait_timeout = 600
max_allowed_packet = 16M
thread_cache_size = 128
sort_buffer_size = 4M
bulk_insert_buffer_size = 16M
tmp_table_size = 32M
max_heap_table_size = 32M
#
# * MyISAM
#
# นี่แทนที่สคริปต์เริ่มต้นและตรวจสอบตาราง MyISAM หากจำเป็น
# ครั้งแรกที่สัมผัส เมื่อเกิดข้อผิดพลาดให้คัดลอกและลองซ่อมแซม
myisam_recover = BACKUP
key_buffer_size = 128M
# open-files-limit = 2000
table_open_cache = 400
myisam_sort_buffer_size = 512M
concurrent_insert = 2
read_buffer_size = 2M
read_rnd_buffer_size = 1M
#
# * การกำหนดค่าแคชข้อความค้นหา
#
# แคชเพียงชุดผลลัพธ์เล็ก ๆ เท่านั้นดังนั้นเราจึงสามารถใส่แคชแบบสอบถามได้มากขึ้น
query_cache_limit = 128K
query_cache_size = 64M
# สำหรับการตั้งค่าการเขียนเพิ่มเติมให้ตั้งค่าเป็น DEMAND หรือ OFF
#query_cache_type = DEMAND
#
# * การบันทึกและการจำลองแบบ
#
# สถานที่ทั้งสองได้รับการหมุนโดย cronjob
# โปรดทราบว่าประเภทบันทึกนี้เป็นตัวทำลายประสิทธิภาพ
# ตั้งแต่ 5.1 คุณสามารถเปิดใช้งานบันทึกที่รันไทม์!
#general_log_file = /var/log/mysql/mysql.log
#general_log = 1
#
# การบันทึกข้อผิดพลาดไปที่ syslog เนื่องจาก /etc/mysql/conf.d/mysqld_safe_syslog.cnf
#
# เราต้องการทราบเกี่ยวกับข้อผิดพลาดของเครือข่ายและเช่นนั้น
log_warnings = 2
#
# เปิดใช้งานบันทึกคิวรีช้าเพื่อดูคิวรีที่มีระยะเวลายาวนานโดยเฉพาะ
#slow_query_log [= {0 | 1}]
slow_query_log_file = /var/log/mysql/mariadb-slow.log
long_query_time = 10
#log_slow_rate_limit = 1,000
log_slow_verbosity = query_plan

# เข้าสู่ระบบคำสั่งที่ไม่ใช้ดัชนี
#log_slow_admin_statements
#
# ต่อไปนี้สามารถใช้เป็นเรื่องง่ายในการเล่นซ้ำบันทึกการสำรองข้อมูลหรือการจำลองแบบ
# note: หากคุณกำลังตั้งค่าการจำลองแบบทาสดู README.Debian เกี่ยวกับ
# การตั้งค่าอื่น ๆ ที่คุณอาจจำเป็นต้องเปลี่ยน
# server-id = 1
#report_host = master1
#auto_increment_increment = 2
#auto_increment_offset = 1
log_bin = / var / log / mysql / mariadb-bin
log_bin_index = /var/log/mysql/mariadb-bin.index
# ไม่ใช่ fab สำหรับประสิทธิภาพ แต่ปลอดภัยกว่า
#sync_binlog = 1
expire_logs_days = 10
max_binlog_size = 100M
# ทาส
#relay_log = / var / log / mysql / relay-bin
#relay_log_index = /var/log/mysql/relay-bin.index
#relay_log_info_file = /var/log/mysql/relay-bin.info
#log_slave_updates
#อ่านเท่านั้น
#
# หากแอปพลิเคชันรองรับมัน sql_mode ที่เข้มงวดนี้จะป้องกันบางอย่าง
# ข้อผิดพลาดเช่นการแทรกวันที่ไม่ถูกต้องเป็นต้น
#sql_mode = NO_ENGINE_SUBSTITUTION ดั้งเดิม
#
# * InnoDB
#
# InnoDB ถูกเปิดใช้งานโดยค่าเริ่มต้นโดยมีไฟล์ 10MB ใน / var / lib / mysql /
# อ่านคู่มือสำหรับตัวเลือกที่เกี่ยวข้องกับ InnoDB เพิ่มเติม มีมากมาย!
default_storage_engine = InnoDB
# คุณไม่สามารถเปลี่ยนขนาดไฟล์บันทึกได้ต้องใช้ขั้นตอนพิเศษ
#innodb_log_file_size = 50M
innodb_buffer_pool_size = 20G
innodb_log_buffer_size = 8M
innodb_file_per_table = 1
innodb_open_files = 400
innodb_io_capacity = 400
innodb_flush_method = O_DIRECT
#
# * คุณสมบัติด้านความปลอดภัย
#
# อ่านคู่มือด้วยถ้าคุณต้องการ chroot!
# chroot = / var / lib / mysql /
#
# สำหรับการสร้างใบรับรอง SSL ฉันขอแนะนำ OpenSSL GUI "tinyca"
#
# ssl-ca = / etc / mysql / cacert.pem
# ssl-cert = / etc / mysql / server-cert.pem
# ssl-key = / etc / mysql / server-key.pem



mysqldump []
รวดเร็ว
ชื่ออ้าง
max_allowed_packet = 16M

[MySQL]
# no-auto-rehash # เริ่มเร็วขึ้นของ mysql แต่ไม่มีการเติมเต็มของแท็บ

[isamchk]
key_buffer = 16M

#
# * สำคัญ: การตั้งค่าเพิ่มเติมที่สามารถลบล้างไฟล์เหล่านี้ได้!
# ไฟล์จะต้องลงท้ายด้วย '.cnf' มิฉะนั้นไฟล์จะถูกละเว้น
#
! includedir /etc/mysql/conf.d/

และเนื้อหาของตัวแปร inno:

MariaDB [(ไม่มี)]> แสดงความหลากหลายเช่น 'inno%';
+ + ------------------------------------------- ----- ------------------- +
| Variable_name | ค่า |
+ + ------------------------------------------- ----- ------------------- +
| innodb_adaptive_flushing | ON |
| innodb_adaptive_flushing_lwm | 10 |
| innodb_adaptive_hash_index | ON |
| innodb_adaptive_hash_index_partitions | 1 |
| innodb_adaptive_max_sleep_delay | 150000 |
| innodb_additional_mem_pool_size | 8388608 |
| innodb_api_bk_commit_interval | 5 |
| innodb_api_disable_rowlock | ปิด |
| innodb_api_enable_binlog | ปิด |
| innodb_api_enable_mdl | ปิด |
| innodb_api_trx_level | 0 |
| innodb_autoextend_increment | 64 |
| innodb_autoinc_lock_mode | 1 |
| innodb_buffer_pool_dump_at_shutdown | ปิด |
| innodb_buffer_pool_dump_now | ปิด |
| innodb_buffer_pool_filename | ib_buffer_pool |
| innodb_buffer_pool_instances | 8 |
| innodb_buffer_pool_load_abort | ปิด |
| innodb_buffer_pool_load_at_startup | ปิด |
| innodb_buffer_pool_load_now | ปิด |
| innodb_buffer_pool_populate | ปิด |
| innodb_buffer_pool_size | 21474836480 |
| innodb_change_buffer_max_size | 25 |
| innodb_change_buffering | ทั้งหมด |
| innodb_checksum_algorithm | Innodb |
| innodb_checksums | ON |
| innodb_cleaner_lsn_age_factor | high_checkpoint |
| innodb_cmp_per_index_enabled | ปิด |
| innodb_commit_concurrency | 0 |
| innodb_compression_failure_threshold_pct | 5 |
| innodb_compression_level | 6 |
| innodb_compression_pad_pct_max | 50 |
| innodb_concurrency_tickets | 5000 |
| innodb_corrupt_table_action | ยืนยัน |
| innodb_data_file_path | ibdata1: 12M: autoextend |
| innodb_data_home_dir | |
| innodb_disable_sort_file_cache | ปิด |
| innodb_doublewrite | ON |
| innodb_empty_free_list_algorithm | backoff
| innodb_fake_changes | ปิด |
| innodb_fast_shutdown | 1 |
| innodb_file_format | ละมั่ง
| innodb_file_format_check | ON |
| innodb_file_format_max | ละมั่ง
| innodb_file_per_table | ON |
| innodb_flush_log_at_timeout | 1 |
| innodb_flush_log_at_trx_commit | 1 |
| innodb_flush_method | O_DIRECT |
| innodb_flush_neighbors | 1 |
| innodb_flushing_avg_loops | 30 |
| innodb_force_load_corrupted | ปิด |
| innodb_force_recovery | 0 |
| innodb_foreground_preflush | exponential_backoff |
| innodb_ft_aux_table | |
| innodb_ft_cache_size | 8000000 |
| innodb_ft_enable_diag_print | ปิด |
| innodb_ft_enable_stopword | ON |
| innodb_ft_max_token_size | 84 |
| innodb_ft_min_token_size | 3 |
| innodb_ft_num_word_optimize | 2000 |
| innodb_ft_result_cache_limit | 2000000000 |
| innodb_ft_server_stopword_table | |
| innodb_ft_sort_pll_degree | 2 |
| innodb_ft_total_cache_size | 640000000 |
| innodb_ft_user_stopword_table | |
| innodb_io_capacity | 400 |
| innodb_io_capacity_max | 2000 |
| innodb_kill_idle_transaction | 0 |
| innodb_large_prefix | ปิด |
| innodb_lock_wait_timeout | 50 |
| innodb_locking_fake_changes | ON |
| innodb_locks_unsafe_for_binlog | ปิด |
| innodb_log_arch_dir | ./ |
| innodb_log_arch_expire_sec | 0 |
| innodb_log_archive | ปิด |
| innodb_log_block_size | 512 |
| innodb_log_buffer_size | 8388608 |
| innodb_log_checksum_algorithm | Innodb |
| innodb_log_compressed_pages | ON |
| innodb_log_file_size | 50331648 |
| innodb_log_files_in_group | 2 |
| innodb_log_group_home_dir | ./ |
| innodb_lru_scan_depth | 1024 |
| innodb_max_bitmap_file_size | 104857600 |
| innodb_max_changed_pages | 1000000 |
| innodb_max_dirty_pages_pct | 75 |
| innodb_max_dirty_pages_pct_lwm | 0 |
| innodb_max_purge_lag | 0 |
| innodb_max_purge_lag_delay | 0 |
| innodb_mirrored_log_groups | 1 |
| innodb_monitor_disable | |
| innodb_monitor_enable | |
| innodb_monitor_reset | |
| innodb_monitor_reset_all | |
| innodb_old_blocks_pct | 37 |
| innodb_old_blocks_time | 1,000 |
| innodb_online_alter_log_max_size | 134217728 |
| innodb_open_files | 400 |
| innodb_optimize_fulltext_only | ปิด |
| innodb_page_size | 16384 |
| innodb_print_all_deadlocks | ปิด |
| innodb_purge_batch_size | 300 |
| innodb_purge_threads | 1 |
| innodb_random_read_ahead | ปิด |
| innodb_read_ahead_threshold | 56 |
| innodb_read_io_threads | 4 |
| innodb_read_only | ปิด |
| innodb_replication_delay | 0 |
| innodb_rollback_on_timeout | ปิด |
| innodb_rollback_segments | 128 |
| innodb_sched_priority_cleaner | 19 |
| innodb_show_locks_held | 10 |
| innodb_show_verbose_locks | 0 |
| innodb_sort_buffer_size | 1048576 |
| innodb_spin_wait_delay | 6 |
| innodb_stats_auto_recalc | ON |
| innodb_stats_method | nulls_equal |
| innodb_stats_on_metadata | ปิด |
| innodb_stats_persistent | ON |
| innodb_stats_persistent_sample_pages | 20 |
| innodb_stats_sample_pages | 8 |
| innodb_stats_transient_sample_pages | 8 |
| innodb_status_output | ปิด |
| innodb_status_output_locks | ปิด |
| innodb_strict_mode | ปิด |
| innodb_support_xa | ON |
| innodb_sync_array_size | 1 |
| innodb_sync_spin_loops | 30 |
| innodb_table_locks | ON |
| innodb_thread_concurrency | 0 |
| innodb_thread_sleep_delay | 10000 |
| innodb_track_changed_pages | ปิด |
| innodb_undo_directory | . |
| innodb_undo_logs | 128 |
| innodb_undo_tablespaces | 0 |
| innodb_use_atomic_writes | ปิด |
| innodb_use_fallocate | ปิด |
| innodb_use_global_flush_log_at_trx_commit | ON |
| innodb_use_native_aio | ON |
| innodb_use_stacktrace | ปิด |
| innodb_use_sys_malloc | ON |
| innodb_version | 5.6.17-65.0 |
| innodb_write_io_threads | 4 |
+ + ------------------------------------------- ----- ------------------- +
143 แถวในชุด (0.02 วินาที)

จำนวนแกนของเครื่องคือ 8 มันเป็น

Intel(R) Xeon(R) CPU E3-1246 v3 @ 3.50GHz ณ วันที่ /proc/cpuinfo

หนึ่งบันทึกล่าสุด: เรียกใช้แบบสอบถามด้วยดัชนีที่แนะนำโดย RolandoMYSQLDBA และแบบสอบถามใช้เวลาประมาณ 11-20 วินาที ฉันต้องการชี้ให้เห็นว่าเป็นสิ่งสำคัญสำหรับฉัน (นี่คือตารางหลักของกระดานข่าว) ที่การสืบค้นแรกเกี่ยวกับเธรดที่ส่งกลับในเวลาน้อยกว่าหนึ่งวินาทีเนื่องจากมีมากกว่า 60.000 เธรดและ google-bots รวบรวมข้อมูลอย่างต่อเนื่อง หัวข้อเหล่านี้


ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม บทสนทนานี้ถูกย้ายไปแชทย้ายไปแชท
พอลไวท์พูดว่า GoFundMonica

คำตอบ:


24

คำถามของคุณ

SELECT post.postid, post.attach FROM newbb_innopost AS post WHERE post.threadid = 51506;

เมื่อมองอย่างรวดเร็วครั้งแรกแบบสอบถามนั้นควรแตะ 1.1597% (62510 จาก 5390146) ของตาราง มันควรจะได้รับการกระจายที่สำคัญของ threadid 51506 อย่างรวดเร็ว

ตรวจสอบความเป็นจริง

ไม่ว่าคุณใช้ MySQL รุ่นใด (Oracle, Percona, MariaDB) พวกเขาไม่มีใครสามารถต่อสู้กับศัตรูคนใดคนหนึ่งที่พวกเขามีเหมือนกัน: The InnoDB Architecture

สถาปัตยกรรม InnoDB

ดัชนีกลุ่มข้อมูล

โปรดทราบว่าแต่ละรายการ threadid มีคีย์หลักที่แนบมา ซึ่งหมายความว่าเมื่อคุณอ่านจากดัชนีจะต้องทำการค้นหาคีย์หลักภายในClusteredIndex (ชื่อภายใน gen_clust_index) ใน ClusteredIndex แต่ละหน้า InnoDB มีทั้งข้อมูลและข้อมูลดัชนีคีย์หลัก ดูโพสต์ของฉันที่ดีที่สุดของ MyISAM และ InnoDBสำหรับข้อมูลเพิ่มเติม

ดัชนีซ้ำซ้อน

คุณมีความยุ่งเหยิงจำนวนมากในตารางเนื่องจากดัชนีบางรายการมีคอลัมน์นำหน้าเหมือนกัน MySQL และ InnoDB ต้องเลื่อนดูดัชนีเพื่อให้ได้โหนด BTREE ที่ต้องการ คุณควรลดความยุ่งเหยิงนั้นด้วยการทำสิ่งต่อไปนี้:

ALTER TABLE newbb_innopost
    DROP INDEX threadid,
    DROP INDEX threadid_2,
    DROP INDEX threadid_visible_dateline,
    ADD INDEX threadid_visible_dateline_index (`threadid`,`visible`,`dateline`,`userid`)
;

ทำไมต้องตัดดัชนีเหล่านี้ออก

  • ดัชนีสามตัวแรกเริ่มต้นด้วย threadid
  • threadid_2 และ threadid_visible_datelineเริ่มต้นด้วยสามคอลัมน์เดียวกัน
  • threadid_visible_dateline ไม่ต้องการ postid เนื่องจากเป็นคีย์หลักและฝังอยู่

บัฟเฟอร์ที่บัฟเฟอร์

InnoDB Buffer Pool แคชข้อมูลและหน้าดัชนี MyISAM แคชหน้าดัชนีเท่านั้น

เพียงแค่ในพื้นที่นี้เพียงอย่างเดียว MyISAM ไม่ต้องเสียเวลาในการแคชข้อมูล นั่นเป็นเพราะมันไม่ได้ออกแบบมาเพื่อแคชข้อมูล InnoDB เก็บแคชทุกหน้าข้อมูลและหน้าดัชนี (และคุณยายของมัน) ที่สัมผัส หาก InnoDB Buffer Pool ของคุณมีขนาดเล็กเกินไปคุณอาจแคชหน้าการทำให้เพจใช้ไม่ได้และลบหน้าทั้งหมดในการสืบค้นเดียว

เค้าโครงตาราง

คุณสามารถโกนหนวดของพื้นที่บางส่วนจากแถวโดยการพิจารณาและimportthreadid importpostidคุณมีพวกเขาเป็น BIGINT พวกเขาใช้เวลาถึง 16 ไบต์ใน ClusteredIndex ต่อแถว

คุณควรใช้สิ่งนี้

SELECT importthreadid,importpostid FROM newbb_innopost PROCEDURE ANALYSE();

สิ่งนี้จะแนะนำว่าควรใช้คอลัมน์ประเภทใดสำหรับชุดข้อมูลที่ระบุ

สรุปผลการศึกษา

MyISAM มีส่วนร่วมน้อยกว่า InnoDB โดยเฉพาะในพื้นที่แคช

ในขณะที่คุณเปิดเผยจำนวน RAM ( 32GB) และเวอร์ชั่นของ MySQL ( Server version: 10.0.12-MariaDB-1~trusty-wsrep-log mariadb.org binary distribution, wsrep_25.10.r4002) ยังมีชิ้นส่วนอื่น ๆ สำหรับปริศนานี้ที่คุณยังไม่ได้เปิดเผย

  • การตั้งค่า InnoDB
  • จำนวนแกน
  • การตั้งค่าอื่น ๆ จาก my.cnf

หากคุณสามารถเพิ่มสิ่งเหล่านี้ลงในคำถามฉันสามารถอธิบายเพิ่มเติมได้

อัพเดท 2014-08-28 11:27 EDT

คุณควรเพิ่มเธรด

innodb_read_io_threads = 64
innodb_write_io_threads = 16
innodb_log_buffer_size = 256M

ฉันจะพิจารณาปิดการใช้งานแคชแบบสอบถาม (ดูโพสต์ล่าสุดของฉันทำไม query_cache_type ถูกปิดใช้งานโดยเริ่มต้นเริ่มต้นจาก MySQL 5.6? )

query_cache_size = 0

ฉันจะเก็บบัฟเฟอร์พูลไว้

innodb_buffer_pool_dump_at_shutdown=1
innodb_buffer_pool_load_at_startup=1

เพิ่มการล้างเธรด (ถ้าคุณทำ DML บนหลาย ๆ ตาราง)

innodb_purge_threads = 4

ให้มันลอง !!!


ฉันรู้ว่า InnoDB นั้นตั้งใจให้ช้าลงในการทดสอบความเร็วบริสุทธิ์ แต่จนถึงระดับนี้ ฉันอ่านแล้วว่าทีม MySQL ทำงานอย่างหนักเพื่อปิดช่องว่างนี้ เรายังคงเพิ่มขึ้นอีกประมาณ 100 เท่า! คำถาม - คุณกำลังบอกว่าการสืบค้นในลักษณะนี้จะได้รับการบริการที่ดีขึ้นด้วยดัชนี B-tree ที่ไม่ใช่แบบ "ตรง" (เช่นไม่มีข้อมูล PK) ถ้าเป็นเช่นนั้นทำไมไม่ / ไม่ได้ถูกนำไปใช้? ฟังก์ชั่นที่ OP ต้องการนั้นไม่ใช่กรณีการใช้งานเล็กน้อย
Vérace

คุณสามารถเพิ่มลิงค์ไปยังรูปภาพขนาดเต็มได้หรือไม่? บางส่วนอ่านได้ยาก :-)
น้ำ

@RolandMySQLDBA ขอบคุณสำหรับข้อมูล - ฉันหวังว่าคุณจะไม่แนะนำให้ชะลอตัว 100x เป็น "ปกติ" สำหรับ InnoDB ... ฉันสามารถมีชีวิตอยู่กับ 2x หรือ 3x แต่ 100x นั้นมากเกินไป ตามที่ร้องขอฉันได้เพิ่มข้อมูลที่หายไปในคำถามของฉัน :) ขอบคุณสำหรับคำอธิบายจนถึงตอนนี้! จำนวนแกนของเครื่องคือ 8
jollyroger

2
@watery นี่คือภาพขนาดเต็ม: scribd.com/doc/31337494/XtraDB-InnoDB-internals-in-drawing
RolandoMySQLDBA

1
ขอบคุณมากสำหรับความช่วยเหลือของคุณ @RolandoMySQLDBA แต่น่าเสียดายที่การปรับแต่งครั้งล่าสุดไม่ได้ช่วยอะไรและ InnoDB ใช้เวลาประมาณ 11-20 วินาทีในการดำเนินการให้เสร็จสมบูรณ์ ฉันลองทำตามคำตอบของคุณ - วางดัชนีทั้งหมดและสร้างดัชนีครอบคลุม นั่นช่วยได้มาก หากไม่มีคำอธิบายของดัชนีของคุณฉันจะไม่พบวิธีแก้ปัญหานี้ จะไปตรวจสอบคำตอบของคุณและเขียนคำตอบของตัวเองอธิบายสิ่งที่ฉันได้ :)
jollyroger

7

@RolandMySQLDBA ได้ให้คำแนะนำที่ถูกต้องในการตอบคำถาม ปัญหาดูเหมือนจะอยู่ในแบบสอบถามและเพื่อให้ผลลัพธ์ที่จะได้รับกลับแต่ละเขตข้อมูลเหล่านั้นจะต้องมีการอ่าน (อย่างใดจากฐานข้อมูล)

ฉันทำดัชนีทั้งหมดลดลง แต่PRIMARY KEYและใส่ดัชนีใหม่นี้:

ALTER TABLE newbb_innopost ADD INDEX threadid_visible_dateline_index (threadid,visible,dateline,userid,attach,ipaddress);

ลิงก์นี้จะอธิบายสิ่งที่เกิดขึ้นที่นี่ ( ครอบคลุมดัชนี ): เขตข้อมูลที่สอบถามของแบบสอบถามซึ่งpostid,attachสามารถแยกได้จากคีย์ ที่บันทึกการตรวจสอบข้อมูลจริงและใช้ I / O กับฮาร์ดดิสก์

แบบสอบถามทั้งหมดตอนนี้ทำงานด้วย 0.00 วินาที .. :)

ขอบคุณมากสำหรับความช่วยเหลือของคุณ

แก้ไข : ปัญหาพื้นฐานที่เกิดขึ้นจริงไม่ได้รับการแก้ไขฉันเพียงแค่หลีกเลี่ยงด้วยเทคนิคนี้ InnoDB ต้องการการแก้ไขที่จริงจังในพื้นที่นี้


ฉันกำลังเผชิญกับปัญหาเดียวกัน ข้อความค้นหา myisma ใช้เวลา 0.01 วินาทีในขณะที่ Innodb ใช้เวลา 60 วินาทีจะลองทำตามคำแนะนำของคุณ
AMB

@AMB - 0.01s มีกลิ่นเหมือน Query cache SQL_NO_CACHEเวลาที่มันอีกครั้งกับ
Rick James

0

จากทั้งคิวรีและตารางดูเหมือนว่าคุณเป็นข้อมูล selectikg จากตารางอนุกรมเวลา ดังนั้นอาจเป็นไปได้ว่าเวลาสอบถามช้าเนื่องจากคุณแทรกพร้อมกันหรือไม่

หากทั้งสองสิ่งเป็นจริงฉันขอแนะนำให้มองหา ScaleDB เป็นทางเลือกได้หรือไม่ คุณจะยังอยู่ใน MariaDB เพียง (อาจ) เป็นเครื่องมือที่เหมาะสมกว่า

http://www.scaledb.com - หน้าแรก http://www.scaledb.com/download-form.php - ผลิตภัณฑ์ของเรา


2
คุณควรเพิ่มว่ารุ่นที่สำคัญไม่ฟรี
ypercubeᵀᴹ

0

ทั้งเครื่องยนต์จะเรียกใช้แบบสอบถามมากได้เร็วขึ้นด้วย

INDEX(threadid, attach, postid)

นี่เป็นเพราะมันจะเป็นดัชนี "ครอบคลุม" และจะทำงานในลักษณะเดียวกัน (โดยใช้ดัชนี BTree)

นอกจากนี้ฉันจะบอกว่ามันเป็นไปไม่ได้สำหรับทั้งเครื่องยนต์บนเซิร์ฟเวอร์ "เย็น":

62510 rows in set (0.13 sec)

กรุณาใช้ SQL_NO_CACHEเมื่อใดก็ตามที่ใช้การกำหนดเวลา - เราไม่ต้องการให้แคชของคิวรีสร้างข้อสรุปที่เป็นมลพิษ

อีกวิธีที่รวดเร็ว (การแคช I / O อย่างไม่สม่ำเสมอ):

ใช้ InnoDB และเปลี่ยนจากPRIMARY KEY (postid)เป็น

PRIMARY KEY(threadid, postid),
INDEX(postid)

เหตุผลก็คือการทำเช่นนี้จะทำให้แถวที่เกี่ยวข้องทั้งหมดอยู่ติดกันดังนั้นจึงต้องใช้ I / O น้อยลง ฯลฯ สิ่งที่INDEX(postid)จะทำให้AUTO_INCREMENTมีความสุข Caveat: นี่ยุ่งกับปุ่มรองทั้งหมด - บางอันจะเร็วกว่าบางอันจะช้ากว่า


0

แม้ว่าจะไม่ตรงกับ @jollyroger โดยตรงเพราะเขามีการตั้งค่าที่ถูกต้องแล้ว แต่ฉันได้รับการปรับปรุงที่สำคัญเปลี่ยนเป็นinnodb_buffer_pool_size70% ของ RAM ของฉันตามที่อธิบายไว้ในทำไม myisam จึงช้ากว่า Innodb

ครั้งแรกMyISAMช้า แต่โอเค แล้วInnoDBทำสิ่งที่ไม่ดีคล้ายกับ 100x ช้าลงในคำถามนี้และหลังการเปลี่ยนการตั้งค่าInnoDBได้ถึง 10 MyISAMเท่าได้เร็วขึ้นแล้ว

การตั้งค่าเริ่มต้นของฉันอยู่ที่ 8MB ซึ่งน้อยไปมาก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.