We many times wrote about InnoDB scalability problems, this time We are faced with one for MyISAM tables. We saw that several times in synthetic benchmarks but never in production, that’s why we did not escalate MyISAM scalability question. This time working on the customer system we figured out that box with 1 CPU Core is able to handle more queries per second than identical box, but with 4 CPU Cores.

The main query which showed this problem was similar to this:

Table t1 contains about 260,000 records, all with stat=1, and t2 contains 4000 records, all with val=5 and different t1_id. It is surely not smart index structure for such data distribution but good enough for performance gotcha illustration purposes.

The benchmark shows following results for MyISAM using MySQL 5.0.45 run on 4 Core System:

Threads queries/sec
1 161
2 107
4 110
8 121
16 138

As you see running 2-4 threads concurrently we get result by 30% worse than with 1 thread, although it’s only simple select query which should be executed without exclusive table locking. Even on 16 threads we’re getting performance worse than with single query.

The problem in this case is key buffer contention which unlike popular belief not fully fixed by changes done in MySQL 4.1

As Monty explained us in MySQL 4.1 the change to key cache locking was done so disk IO is not done while lock is held, while lock is still held when key block is copied to processing thread local storage on Key Read Request. This lock is per key cache so if you have contention while multiple indexes are used you can create multiple key caches and map those to them. In this case however single index had most of the load.

This is partially proved by oprofile data (case with 4 threads):

As you see 40% of effective CPU time is spent in pthread_mutex_lock / pthread_mutex_unlock.

We could not get oprofile call tree to work on this box so we can only guess where these mutex lock requests come from.

A second confirmation that key_cache is a problem is benchmark run with disabled key_cache (=0).

Results for MyISAM with key_buffer_size=0

Threads queries/sec
1 128
2 113
4 193
8 196
16195

The result for 1 thread is decreased and it is expected, but, funny, we have more queries per second for 4, 8, 16 with disabled key_cache.

The results for 2 threads is however quite unexpected. Though we did not have a time to profile it in more details.
Note however even in this case scalability is far from perfect giving only 1.5 times gain with.

The solution we proposed in this case was converting table t1 into InnoDB, and results:

ThreadsQueries/sec
1296
2341
4544
8493
16498

InnoDB both performs much better in this case (not surprisingly as there is a lot of primary key lookups) but its scalability is not perfect giving less than 2x in peak which happens to be at 4 concurrent threads. So there is still work to do in addition to fixes done in later MySQL 5.0 versions.

We also decided to take a time and see may be brand new Falcon (significantly updated in 6.0.2 release) handles this query:

Threads Queries/sec
1 51
279
4116
8142
16 164

As you can see Falcon perform extremely poorly when single query executed being 1/3rd of MyISAM and 1/6th of Innodb. On other hand it scales quite nicely as number of threads increase.

The interesting thing is it shows best performance at 16 threads, showing 50% gain from 4 threads – which is quite unexpected for CPU bound load on system with 4 Cores.

Here is comparison of MyISAM Innodb and Falcon results in the graphical form:

2007-10-12_145302.png

I’ve created bug, for MyISAM key cache contention issue and lets see if there are any plans to have it fixed.

8 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Steven Roussey

Unlike InnoDb, MyISAM loads up the entire row because it may need ‘name’. This is a problem when you have that ‘LIMIT 1206,18’. That first number is too high. You will see far greater performance when you break it into two queries — create a temp table then join. All that (unused) data will ruin the caches. I had a discussion about this with Money back in the 4.0 days. It is just not important enough since they don’t expect people to use high limits like that. And there is the temp table workaround. Might be able to use subselects to avoid the huge data load as well. In my case there were big blobs that got loaded up though they weren’t needed.

Steven Roussey

Monty, not Money! Heh… oops.

Peter Zaitsev

Steven,

The question in this case not the query optimization – we simply took query (a bit obfuscating) from users application and checked how well different storage engines can run it.

But note in this case there is no blobs so both Innodb and MyISAM will need to read full rows. Innodb has advantage doing join via primary key but this is other story.

Steven Roussey

OK, though I would be interested to see if you find any differences if you had your tests run with “LIMIT 18” instead of “LIMIT 1206,18”. I find that a high index into a limit plus a join kills all my caches. But then again, I may have blobs on all the tables involved… !

Michael

Dear Steven,

May I ask how you would re-write that same query into two queries, a temp table, and a join?

I’d like to see the example.

Michael

Morten Olsen

Hi Peter,

Thanks for the information. I’m not sure I fully understand the sentence:

“As Monty explained us in MySQL 4.1 the change to key cache locking was done so disk IO is not done while lock is held, while lock is still held when key block is copied to processing thread local storage on Key Read Request.”

Does that mean that the contention doesn’t happen when the index is read from disk to the buffer, but rather each time some thread try to read from the buffer? Put another way, does the amount of contention relate more to the status variable Key_read_requests rather than Key_reads?

Bests, Morten

Rick

I have been hearing a lot of anecdotal evidence that multiple cores are either bad or not good. Thanks for the detailed analysis of one case. Here are the three main causes of poor performance in multiple cores:

* Query Cache has locking problems. What was the setting for Query Cache? If it was even turned on and not used, then there were a locks there. (Granted, perhaps only 1 per SELECT.)

* Key_buffer has locking problems, as you clearly note.

* InnoDB has malloc problems — the MySQL version is less efficient than plain ‘ole malloc when you have multiple cores.

I wish there would be a fix in 5.1; I don’t want to have to wait for 6.x and Maria and Falcon.

Gunnar

@Peter,

Thanks for the good article.

@Rick,
> I wish there would be a fix in 5.1;
> I don’t want to have to wait for 6.x and Maria and Falcon.
I could be wrong but Peters results do not give me the impression that Maria or Falcon will fix this.