InnoDB 또는 MyISAM 중 어느 것이 더 빠릅니까?


54

MyISAM이 InnoDB보다 "빠른"방법

  • MyISAM은 데이터에 대한 디스크 읽기를 수행해야합니까?
  • InnoDB는 인덱스와 데이터에 버퍼 풀을 사용하고 인덱스에만 MyISAM을 사용합니까?

MyISAM을 사용하면 OS가 데이터 블록을 캐시 할 수 있으므로 항상 "데이터에 대한 디스크 읽기"를 수행 하지는 않습니다 .
Rick James

답변:


68

MyISAM이 InnoDB가이 독특한 상황에있을 때 더 빠를 수있는 유일한 방법

MyISAM

읽을 때 MyISAM 테이블의 인덱스는 .MYI 파일에서 한 번 읽고 myISAM 키 캐시에로드 할 수 있습니다 ( key_buffer_size 크기로 ). MyISAM 테이블의 .MYD를 더 빨리 읽을 수있게하려면 어떻게해야합니까? 이것으로 :

ALTER TABLE mytable ROW_FORMAT=Fixed;

나는 내 과거 게시물에 이것에 대해 썼다

InnoDB

좋아, InnoDB는 어때? InnoDB는 쿼리를위한 디스크 I / O를 수행합니까? 놀랍게도 그렇습니다! 당신은 아마 그 말에 미쳤다고 생각하고 있지만 SELECT 쿼리조차도 사실 입니다. 이 시점에서 "InnoDB가 어떻게 쿼리를위한 디스크 I / O를 수행 하는가?"

모든 것은 ACID 호환 트랜잭션 스토리지 엔진 인 InnoDB로 되돌아갑니다 . InnoDB가 Transactional이 되려면 Iin 을 지원해야합니다 ACID. 트랜잭션 격리를 유지하는 기술은 MVCC, Multiversion Concurrency Control을 통해 수행됩니다 . 간단히 말해 InnoDB는 트랜잭션이 데이터를 변경하기 전에 어떤 데이터가 나타나는지 기록합니다. 그것은 어디에 기록됩니까? 시스템 테이블 스페이스 파일에서 ibdata1로 더 잘 알려져 있습니다. 이를 위해서는 디스크 I / O가 필요합니다 .

비교

InnoDB와 MyISAM은 모두 디스크 I / O를 수행하기 때문에 누가 더 빠른지 어떤 요소가 영향을 미칩니 까?

  • 열 크기
  • 열 형식
  • 문자 세트
  • 숫자 값의 범위 (충분히 큰 INT가 필요함)
  • 블록에서 분할되는 행 (행 체인)
  • DELETEs와 에 의해 발생하는 데이터 조각화UPDATEs
  • 기본 키 크기 (InnoDB에 클러스터 된 인덱스가 있으며 두 개의 키 조회가 필요함)
  • 인덱스 항목의 크기
  • 목록은 계속됩니다 ...

따라서 읽기가 많은 환경에서는 트랜잭션 동작을 지원하기 위해 ibdata1에 포함 된 실행 취소 로그에 기록 된 데이터가 충분한 경우 고정 행 형식의 MyISAM 테이블이 InnoDB 버퍼 풀에서 InnoDB 읽기보다 성능이 뛰어납니다. InnoDB 데이터에 부과됩니다.

결론

데이터 유형, 쿼리 및 스토리지 엔진을 실제로 신중하게 계획하십시오. 데이터가 커지면 데이터 이동이 매우 어려워 질 수 있습니다. 그냥 페이스 북에게 물어보세요 ...


1
훌륭한 답변, 롤란도. 마이클 스톤 브레이커 (Michael Stonebreaker)가 자신의 제품을 팔려고하는데 페이스 북에 대해 전혀 모른다는 놀라운 주장을 포함시키는 것에 대해 의문을 제기해야합니다. MySQL에 대한 Facebook의 발표를 여러 번 들었으므로 그들이 선택한 것에 편안하다는 것이 분명합니다.
Aaron Brown

@AaronBrown 작년 Percona Live NYC에서 Harrison Fisk의 이야기를 들었습니다. Facebook은 InnoDB를 독점적으로 사용하고 온라인 스키마 변경 시스템을 수행하는 방법을 생각해내는 데 시간이 얼마나 걸리는지를 매우 기쁘게 생각합니다. 그는 청중에게 빅 데이터를 다루는 Facebook에서 일할 수있는 기회를 제공합니다. 나는 일부 사람들이 그것에 대해 두려움을 가지고 있음을 보여주기 위해 기사를 포함시켰다. 거대한 데이터로 작업 할 수있는 기회를 환영합니다. 재미 있고 도전적입니다. 배워야 할 기술을 상상해보십시오. 물론, 나는 남은 생애 동안 MyISAM을 건드리지 않을 것입니다 ...
RolandoMySQLDBA

나는 또한 그 회의에 있었고 (이야기를 할 수있는 행운을 가졌다) 해리슨의 발표는 훌륭했다.
Aaron Brown

20

간단한 세상에서는 MyISAM이 읽기 속도가 빠르고 InnoDB가 쓰기 속도가 빠릅니다.

혼합 읽기 / 쓰기 도입을 시작하면 행 잠금 메커니즘 덕분에 InnoDB의 읽기 속도도 빨라집니다.

나는 몇 년 전에 MySQL 스토리지 엔진을 비교해 봤는데 , 오늘날에도 여전히 유효하며 MyISAM과 InnoDB의 고유 한 차이점을 설명합니다.

필자의 경험에 따르면 손상으로 인한 데이터 손실이 중요하지 않은 읽기 용량이 큰 캐시 테이블을 제외한 모든 용도로 InnoDB를 사용해야합니다.


4
이 답변은 5 년이 지났습니다. InnoDB는 거의 모든 방식으로 따라 잡았습니다. 더 이상 MyISAM 사용에 대한 논쟁이 없습니다. MySQL 8.0은 MyISAM을 모두 제거 하는 중입니다.
Rick James

2
링크는 이제 9 년이되었습니다.
Rick James

수정, 대답은 구식 9 년 (첫 번째 문장을 읽는 사람은 데이터베이스를 수행 할 때 심각한 문제가 생길 것입니다)이며 링크는 구식입니다. Rick James를 따라 잡으십시오.
CYREX

1
당신은 맞습니다 @CYREX :-)이 포스트가 11 년 후에도 여전히 트래픽을 받고 있다는 것은 놀라운 일입니다. 제 인생과 InnoDB가 최적화되는 방식 모두에서 많은 변화가있었습니다. 요즘 MyISAM을 사용하는 것이 타당하지 않습니다
Mike Peters

나는 오늘 죽어가는 데이터베이스를 살펴 봐야했고 두 엔진 모두 여전히 이전 버전의 mysql과 함께 사용 중입니다. 테이블은 InnoDB와 MyISAM이며 호기심으로 인해이 게시물에 도움이되었습니다.
Farrukh Subhani

14

두 엔진 간의 기계적 차이를 다루는 여기에 응답을 추가하기 위해 경험적 속도 비교 연구를 제시합니다.

순수한 속도면에서, MyISAM이 InnoDB보다 빠른 경우는 아니지만 제 경험상 PURE READ 작업 환경에서는 약 2.0-2.5 배 더 빠릅니다. 다른 환경이 작성한 것처럼 MyISAM에는 트랜잭션 및 외래 키와 같은 기능이 없습니다.

아래에서 약간의 벤치마킹을 수행했습니다. 루핑에는 파이썬을 사용하고 타이밍 비교에는 timeit 라이브러리를 사용했습니다. 관심을 끌기 위해 메모리 엔진도 포함 시켰습니다.이 기능은 작은 테이블에만 적합하지만 The table 'tbl' is fullMySQL 메모리 제한을 초과 하면 계속 발생 합니다. 내가 본 네 가지 유형의 선택은 다음과 같습니다.

  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'및 'memory'로 대체되었습니다.

 

1) 바닐라 선택

질문: SELECT * FROM tbl WHERE index_col = xx

결과 : 추첨

다른 데이터베이스 엔진별로 바닐라 선택 비교

이것들의 속도는 대체로 동일하며, 예상되는 바와 같이 선택 될 열의 수는 선형입니다. 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이 승리

다른 데이터베이스 엔진에 의한 카운트 비교

이것은 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이 승리

다른 데이터베이스 엔진 별 조건부 선택 비교

여기서 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가 승리

이 쿼리에서는 하위 선택을위한 추가 테이블 세트를 작성했습니다. 각각은 기본 키 인덱스가 있고 다른 하나는 인덱스가없는 BIGINT의 두 열입니다. 큰 테이블 크기로 인해 메모리 엔진을 테스트하지 않았습니다. 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으로 유지하고 대신 하위 선택된 열의 크기를 변경합니다.

다른 데이터베이스 엔진에 의한 하위 선택 비교

여기서 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) )

이 모든 것의 핵심 메시지는 속도에 정말로 관심 이 있다면 어떤 엔진이 더 적합한 지에 대한 가정을하기보다는 수행중인 쿼리를 벤치 마크해야한다는 것입니다.


1
나는 당신이 벤치마킹하고 결정하는 사람에게 유리하기 때문에 당신의 대답을 좋아합니다. 두 개의 시스템이 다른 스토리지 엔진에서 동일한 방식으로 혜택을받지 않으며 스토리지 엔진을 선택하기 위해 실사가 필요합니다. 당신을 위해 +1하고 DBA StackExchange에 오신 것을 환영합니다 !!!
RolandoMySQLDBA

1
또한 다른 게시물 과 함께 내 게시물 dba.stackexchange.com/questions/1/… 을 참조하십시오 . 당신의 포스트 종류는 넘어갑니다.
RolandoMySQLDBA

SELECT * FROM tbl WHERE index_col = xx-그래프에서 더 많은 변형을 일으킬 수있는 두 가지 요소가 있습니다. 기본 키와 보조 키; 인덱스는 캐시되지 않습니다.
Rick James

2
SELECT COUNT(*)WHERE조항 을 추가 할 때까지 MyISAM의 확실한 승자입니다 .
Rick James

필자의 요점은 각 쿼리를 별도로 벤치마킹해야한다는 것입니다. 답변에 코드를 포함 시켰습니다. 다른 검색어를 시험하려면 내 손님이 되십시오. 또는 원하는 검색어를 명시 적으로 추가하고 추가하겠습니다.
StackG

4

어느 것이 더 빠릅니까? 더 빠를 수도 있습니다. YMMV.

어느 것을 사용해야합니까? InnoDB-충돌 안전 등


"등 등"을 정의하십시오
dellasavia

1
@dellasavia-가장 최근의 "etc"는 Oracle이 MyISAM을 제거 할 계획이라는 것입니다. 그들은 InnoDB를 확신합니다.
Rick James
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.