it-swarm.asia

أيهما أسرع ، InnoDB أو MyISAM؟

كيف يمكن أن يكون MyISAM "أسرع" من InnoDB إذا

  • يحتاج MyISAM للقيام يقرأ القرص للبيانات؟
  • يستخدم InnoDB تجمع المخزن المؤقت للفهارس والبيانات ، و MyISAM فقط للفهرس؟
56
jcho360

الطريقة الوحيدة التي يمكن أن يكون بها MyISAM أسرع من أن يكون InnoDB تحت هذا الظرف الفريد

MyISAM

عند القراءة ، يمكن قراءة فهارس جدول MyISAM مرة واحدة من ملف .MYI وتحميلها في ذاكرة التخزين المؤقتة لـ MyISAM (حسب الحجم key_buffer_size ). كيف يمكنك جعل .MYD جدول MyISAM أسرع للقراءة؟ مع هذا:

ALTER TABLE mytable ROW_FORMAT=Fixed;

لقد كتبت عن هذا في مشاركاتي السابقة

InnoDB

حسنًا ، ماذا عن InnoDB؟ هل يقوم InnoDB بعمل أي قرص I/O للاستعلامات؟ من المدهش ، نعم فعلا !! ربما تفكر في أنني مجنون لقول ذلك ، ولكن هذا صحيح تمامًا ، حتى بالنسبة لاستعلامات SELECT . في هذه المرحلة ، ربما تتساءل "كيف يقوم InnoDB في العالم بإدخال القرص I/O للاستعلامات؟"

كل شيء يعود إلى InnoDB كونه [~ # ~] حمض [~ # ~] - شكوى محرك تخزين المعاملات. لكي يكون InnoDB معاملات ، يجب أن يدعم I في ACID ، وهو Isolation. يتم إجراء تقنية الحفاظ على العزلة للمعاملات عبر MVCC ، التحكم في التزامن المتعدد) . بعبارات بسيطة ، يسجل InnoDB كيف تبدو البيانات قبل أن تحاول المعاملات تغييرها. أين يتم تسجيل ذلك؟ في ملف مساحة جدول النظام ، المعروف باسم ibdata1. يتطلب القرص I/O .

مقارنة [~ # ~] [~ # ~]

نظرًا لأن كل من InnoDB و MyISAM يقومان بإدخال/إخراج القرص ، فما العوامل العشوائية التي تملي من هو الأسرع؟

  • حجم الأعمدة
  • تنسيق العمود
  • مجموعات الأحرف
  • نطاق من القيم الرقمية (تتطلب INTs كبيرة بما يكفي)
  • تقسيم الصفوف عبر الكتل (تسلسل الصفوف)
  • تجزئة البيانات بسبب DELETEs و UPDATEs
  • حجم المفتاح الأساسي (يحتوي InnoDB على فهرس مجمع ، يتطلب بحثين رئيسيين)
  • حجم إدخالات الفهرس
  • والقائمة تطول...

وبالتالي ، في بيئة شديدة القراءة ، من الممكن لجدول MyISAM مع تنسيق صف ثابت أن يتفوق على قراءة InnoDB خارج InnoDB Buffer Pool إذا كانت هناك بيانات كافية تتم كتابتها في سجلات التراجع الموجودة داخل ibdata1 لدعم سلوك المعاملات المفروضة على بيانات InnoDB.

[~ # ~] استنتاج [~ # ~]

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

69
RolandoMySQLDBA

في عالم بسيط ، MyISAM أسرع للقراءة ، InnoDB أسرع للكتابة.

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

لقد كتبت مقارنة محركات تخزين MySQL قبل بضع سنوات ، والتي لا تزال سارية حتى يومنا هذا ، تحدد الاختلافات الفريدة بين MyISAM و InnoDB.

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

22
Mike Peters

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

من حيث السرعة الخالصة ، ليس الحال دائمًا أن MyISAM أسرع من InnoDB ولكن في تجربتي تميل إلى أن تكون أسرع لبيئات عمل PURE READ بعامل يبلغ حوالي 2.0-2.5 مرة. من الواضح أن هذا ليس مناسبًا لجميع البيئات - كما كتب آخرون ، تفتقر MyISAM إلى أشياء مثل المعاملات والمفاتيح الخارجية.

لقد قمت ببعض القياس المعياري أدناه - لقد استخدمت python للتكرار ومكتبة الوقت لمقارنات التوقيت. وللاهتمام ، قمت أيضًا بتضمين محرك الذاكرة ، وهذا يوفر أفضل أداء عبر اللوحة على الرغم من أنها مناسبة فقط للجداول الأصغر (أنت تواجه باستمرار The table 'tbl' is full عندما تتجاوز حد ذاكرة MySQL). الأنواع الأربعة التي حددتها هي:

  1. يختار الفانيليا
  2. العد
  3. التحديدات الشرطية
  4. تحديدات فرعية مفهرسة وغير مفهرسة

أولاً ، أنشأت ثلاثة جداول باستخدام SQL التالية

CREATE TABLE
    data_interrogation.test_table_myisam
    (
        index_col BIGINT NOT NULL AUTO_INCREMENT,
        value1 DOUBLE,
        value2 DOUBLE,
        value3 DOUBLE,
        value4 DOUBLE,
        PRIMARY KEY (index_col)
    )
    ENGINE=MyISAM DEFAULT CHARSET=utf8

مع استبدال "MyISAM" بـ "InnoDB" و "الذاكرة" في الجدولين الثاني والثالث.

1) يختار الفانيليا

استعلام: SELECT * FROM tbl WHERE index_col = xx

النتيجة: رسم

Comparison of Vanilla selects by different database engines

سرعة كل هذه هي نفسها على نطاق واسع ، وكما هو متوقع خطي في عدد الأعمدة التي سيتم تحديدها. يبدو InnoDB قليلاً أسرع من MyISAM ولكن هذا هامشي حقًا.

الشفرة:

import timeit
import MySQLdb
import MySQLdb.cursors
import random
from random import randint

db = MySQLdb.connect(Host="...", user="...", passwd="...", db="...", cursorclass=MySQLdb.cursors.DictCursor)
cur = db.cursor()

lengthOfTable = 100000

# Fill up the tables with random data
for x in xrange(lengthOfTable):
    Rand1 = random.random()
    Rand2 = random.random()
    Rand3 = random.random()
    Rand4 = random.random()

    insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(Rand1) + "," + str(Rand2) + "," + str(Rand3) + "," + str(Rand4) + ")"
    insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(Rand1) + "," + str(Rand2) + "," + str(Rand3) + "," + str(Rand4) + ")"
    insertString3 = "INSERT INTO test_table_memory (value1,value2,value3,value4) VALUES (" + str(Rand1) + "," + str(Rand2) + "," + str(Rand3) + "," + str(Rand4) + ")"

    cur.execute(insertString)
    cur.execute(insertString2)
    cur.execute(insertString3)

db.commit()

# Define a function to pull a certain number of records from these tables
def selectRandomRecords(testTable,numberOfRecords):

    for x in xrange(numberOfRecords):
        Rand1 = randint(0,lengthOfTable)

        selectString = "SELECT * FROM " + testTable + " WHERE index_col = " + str(Rand1)
        cur.execute(selectString)

setupString = "from __main__ import selectRandomRecords"

# Test time taken using timeit
myisam_times = []
innodb_times = []
memory_times = []

for theLength in [3,10,30,100,300,1000,3000,10000]:

    innodb_times.append( timeit.timeit('selectRandomRecords("test_table_innodb",' + str(theLength) + ')', number=100, setup=setupString) )
    myisam_times.append( timeit.timeit('selectRandomRecords("test_table_myisam",' + str(theLength) + ')', number=100, setup=setupString) )
    memory_times.append( timeit.timeit('selectRandomRecords("test_table_memory",' + str(theLength) + ')', number=100, setup=setupString) )

2) التهم

الاستعلام: SELECT count(*) FROM tbl

النتيجة: فوز MyISAM

Comparison of counts by different database engines

يوضح هذا الاختلاف الكبير بين MyISAM و InnoDB - MyISAM (والذاكرة) يتتبع عدد السجلات في الجدول ، لذلك هذه المعاملة سريعة و O (1). يزداد مقدار الوقت المطلوب لعد InnoDB بشكل خطي مع حجم الجدول في النطاق الذي بحثت فيه. أظن أن العديد من عمليات التسريع من استعلامات MyISAM التي تتم ملاحظتها في الممارسة ترجع إلى تأثيرات مماثلة.

الشفرة:

myisam_times = []
innodb_times = []
memory_times = []

# Define a function to count the records
def countRecords(testTable):

    selectString = "SELECT count(*) FROM " + testTable
    cur.execute(selectString)

setupString = "from __main__ import countRecords"

# Truncate the tables and re-fill with a set amount of data
for theLength in [3,10,30,100,300,1000,3000,10000,30000,100000]:

    truncateString = "TRUNCATE test_table_innodb"
    truncateString2 = "TRUNCATE test_table_myisam"
    truncateString3 = "TRUNCATE test_table_memory"

    cur.execute(truncateString)
    cur.execute(truncateString2)
    cur.execute(truncateString3)

    for x in xrange(theLength):
        Rand1 = random.random()
        Rand2 = random.random()
        Rand3 = random.random()
        Rand4 = random.random()

        insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(Rand1) + "," + str(Rand2) + "," + str(Rand3) + "," + str(Rand4) + ")"
        insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(Rand1) + "," + str(Rand2) + "," + str(Rand3) + "," + str(Rand4) + ")"
        insertString3 = "INSERT INTO test_table_memory (value1,value2,value3,value4) VALUES (" + str(Rand1) + "," + str(Rand2) + "," + str(Rand3) + "," + str(Rand4) + ")"

        cur.execute(insertString)
        cur.execute(insertString2)
        cur.execute(insertString3)

    db.commit()

    # Count and time the query
    innodb_times.append( timeit.timeit('countRecords("test_table_innodb")', number=100, setup=setupString) )
    myisam_times.append( timeit.timeit('countRecords("test_table_myisam")', number=100, setup=setupString) )
    memory_times.append( timeit.timeit('countRecords("test_table_memory")', number=100, setup=setupString) )

3) تحديد شرطي

استعلام: SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5

النتيجة: فوز MyISAM

Comparison of conditional selects by different database engines

هنا ، تقوم ذاكرة MyISAM والذاكرة بنفس الأداء تقريبًا ، وتتغلب على InnoDB بنحو 50٪ للجداول الأكبر حجمًا. هذا هو نوع الاستعلام الذي يبدو أن فوائد MyISAM يتم تعظيمه.

الشفرة:

myisam_times = []
innodb_times = []
memory_times = []

# Define a function to perform conditional selects
def conditionalSelect(testTable):
    selectString = "SELECT * FROM " + testTable + " WHERE value1 < 0.5 AND value2 < 0.5 AND value3 < 0.5 AND value4 < 0.5"
    cur.execute(selectString)

setupString = "from __main__ import conditionalSelect"

# Truncate the tables and re-fill with a set amount of data
for theLength in [3,10,30,100,300,1000,3000,10000,30000,100000]:

    truncateString = "TRUNCATE test_table_innodb"
    truncateString2 = "TRUNCATE test_table_myisam"
    truncateString3 = "TRUNCATE test_table_memory"

    cur.execute(truncateString)
    cur.execute(truncateString2)
    cur.execute(truncateString3)

    for x in xrange(theLength):
        Rand1 = random.random()
        Rand2 = random.random()
        Rand3 = random.random()
        Rand4 = random.random()

        insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(Rand1) + "," + str(Rand2) + "," + str(Rand3) + "," + str(Rand4) + ")"
        insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(Rand1) + "," + str(Rand2) + "," + str(Rand3) + "," + str(Rand4) + ")"
        insertString3 = "INSERT INTO test_table_memory (value1,value2,value3,value4) VALUES (" + str(Rand1) + "," + str(Rand2) + "," + str(Rand3) + "," + str(Rand4) + ")"

        cur.execute(insertString)
        cur.execute(insertString2)
        cur.execute(insertString3)

    db.commit()

    # Count and time the query
    innodb_times.append( timeit.timeit('conditionalSelect("test_table_innodb")', number=100, setup=setupString) )
    myisam_times.append( timeit.timeit('conditionalSelect("test_table_myisam")', number=100, setup=setupString) )
    memory_times.append( timeit.timeit('conditionalSelect("test_table_memory")', number=100, setup=setupString) )

4) يختار الفرعية

النتيجة: فوز InnoDB

لهذا الاستعلام ، قمت بإنشاء مجموعة إضافية من الجداول للاختيار الفرعي. كل منها عبارة عن عمودين من BIGINTs ، أحدهما بفهرس مفتاح أساسي والآخر بدون أي فهرس. نظرًا لحجم الطاولة الكبير ، لم أختبر محرك الذاكرة. كان أمر إنشاء جدول SQL

CREATE TABLE
    subselect_myisam
    (
        index_col bigint NOT NULL,
        non_index_col bigint,
        PRIMARY KEY (index_col)
    )
    ENGINE=MyISAM DEFAULT CHARSET=utf8;

حيث مرة أخرى ، يتم استبدال "MyISAM" بـ "InnoDB" في الجدول الثاني.

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

Comparison of sub-selects by different database engines

هنا يفوز InnoDB بسهولة. بعد أن نصل إلى جدول حجم معقول ، يتغير كلا المحركين بشكل خطي مع حجم التحديد الفرعي. يسرع الفهرس من أمر MyISAM ولكن من المثير للاهتمام أن له تأثير ضئيل على سرعة InnoDB. subSelect.png

الشفرة:

myisam_times = []
innodb_times = []
myisam_times_2 = []
innodb_times_2 = []

def subSelectRecordsIndexed(testTable,testSubSelect):
    selectString = "SELECT * FROM " + testTable + " WHERE index_col in ( SELECT index_col FROM " + testSubSelect + " )"
    cur.execute(selectString)

setupString = "from __main__ import subSelectRecordsIndexed"

def subSelectRecordsNotIndexed(testTable,testSubSelect):
    selectString = "SELECT * FROM " + testTable + " WHERE index_col in ( SELECT non_index_col FROM " + testSubSelect + " )"
    cur.execute(selectString)

setupString2 = "from __main__ import subSelectRecordsNotIndexed"

# Truncate the old tables, and re-fill with 1000000 records
truncateString = "TRUNCATE test_table_innodb"
truncateString2 = "TRUNCATE test_table_myisam"

cur.execute(truncateString)
cur.execute(truncateString2)

lengthOfTable = 1000000

# Fill up the tables with random data
for x in xrange(lengthOfTable):
    Rand1 = random.random()
    Rand2 = random.random()
    Rand3 = random.random()
    Rand4 = random.random()

    insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(Rand1) + "," + str(Rand2) + "," + str(Rand3) + "," + str(Rand4) + ")"
    insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(Rand1) + "," + str(Rand2) + "," + str(Rand3) + "," + str(Rand4) + ")"

    cur.execute(insertString)
    cur.execute(insertString2)

for theLength in [3,10,30,100,300,1000,3000,10000,30000,100000]:

    truncateString = "TRUNCATE subselect_innodb"
    truncateString2 = "TRUNCATE subselect_myisam"

    cur.execute(truncateString)
    cur.execute(truncateString2)

    # For each length, empty the table and re-fill it with random data
    Rand_sample = sorted(random.sample(xrange(lengthOfTable), theLength))
    Rand_sample_2 = random.sample(xrange(lengthOfTable), theLength)

    for (the_value_1,the_value_2) in Zip(Rand_sample,Rand_sample_2):
        insertString = "INSERT INTO subselect_innodb (index_col,non_index_col) VALUES (" + str(the_value_1) + "," + str(the_value_2) + ")"
        insertString2 = "INSERT INTO subselect_myisam (index_col,non_index_col) VALUES (" + str(the_value_1) + "," + str(the_value_2) + ")"

        cur.execute(insertString)
        cur.execute(insertString2)

    db.commit()

    # Finally, time the queries
    innodb_times.append( timeit.timeit('subSelectRecordsIndexed("test_table_innodb","subselect_innodb")', number=100, setup=setupString) )
    myisam_times.append( timeit.timeit('subSelectRecordsIndexed("test_table_myisam","subselect_myisam")', number=100, setup=setupString) )

    innodb_times_2.append( timeit.timeit('subSelectRecordsNotIndexed("test_table_innodb","subselect_innodb")', number=100, setup=setupString2) )
    myisam_times_2.append( timeit.timeit('subSelectRecordsNotIndexed("test_table_myisam","subselect_myisam")', number=100, setup=setupString2) )

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

14
StackG

وهو أسرع؟ إما أن يكون أسرع. YMMV.

ما الذي يجب عليك استخدامه؟ InnoDB - آمن من التصادم ، إلخ ، إلخ.

4
Rick James