另外一些站点放弃了mysql,采用了NoSQL,比如TokyoCabinet/Tyrant等等。
不可否认,在做一些简单查询(尤其是primary key 查询)的时候,NoSQL比mysql要快很多很多。而且网站上的绝大多数查询都是这样的简单查询。
但是DeNA公司却一直使用mysql和memcached,并且在单台普通服务器上创造了每秒750,000次简单查询的记录。
或许你们不会相信单台mysql可以做到750,000 qps,但这是事实,下面就来详细说说。
SQL真的适合做primary key 查询么?
DeNA 公司的应用经常要进行primary key(PK)查询。比如根据user id取出userinfo,根据diary id取出日志内容, memcached和NoSQL很适合做这种事情。
如果进行memcached 的测试,很有可能每秒可以进行400,000次get操作,即使memcached和client位于不同的服务器。
在一台2.5GHz8核Nehalem 3块千M网卡的服务器上,libmemcached和memcached每秒可以进行420,000次get 操作。
mysql5呢?每秒可以做多少次PK查询?
innodb:
[matsunobu@host ~]$ mysqlslap --query="select user_name,.. from test.user where user_id=1" / --number-of-queries=10000000 --concurrency=30 --host=xxx -uroot -p
此外也可以使用 sysbench或者super-smack等工具进行benchmark。
新开一个shell看一下:
[matsunobu@host ~]$ mysqladmin extended-status -i 1 -r -uroot / | grep -e "Com_select" | Com_select | 107069 | | Com_select | 108873 | | Com_select | 108921 | | Com_select | 109511 | | Com_select | 108084 | | Com_select | 108115 |
100,000 qps 大概是memcached的1/4左右。
为什么会慢这么多呢?
服务器内存足够,这些数据应该都在内存中。
同样是内存操作,为什么mysql比memcached慢这么多?
vmstat数据如下:
[matsunobu@host ~]$ vmstat 1 r b swpd free buff cache in cs us sy id wa st 23 0 0 963004 224216 29937708 58242 163470 59 28 12 0 0 24 0 0 963312 224216 29937708 57725 164855 59 28 13 0 0 19 0 0 963232 224216 29937708 58127 164196 60 28 12 0 0 16 0 0 963260 224216 29937708 58021 165275 60 28 12 0 0 20 0 0 963308 224216 29937708 57865 165041 60 28 12 0 0
%user和%system占用的CPU都相当高。
再看看oprofile统计出来的信息:
ps:这个工具不错,内核级别的。
samples % app name symbol name 259130 4.5199 mysqld MYSQLparse(void*) 196841 3.4334 mysqld my_pthread_fastmutex_lock 106439 1.8566 libc-2.5.so _int_malloc 94583 1.6498 bnx2 /bnx2 84550 1.4748 ha_innodb_plugin.so.0.0.0 ut_delay 67945 1.1851 mysqld _ZL20make_join_statistics P4JOINP10TABLE_LISTP4ItemP16st_dynamic_array 63435 1.1065 mysqld JOIN::optimize() 55825 0.9737 vmlinux wakeup_stack_begin 55054 0.9603 mysqld MYSQLlex(void*, void*) 50833 0.8867 libpthread-2.5.so pthread_mutex_trylock 49602 0.8652 ha_innodb_plugin.so.0.0.0 row_search_for_mysql 47518 0.8288 libc-2.5.so memcpy 46957 0.8190 vmlinux .text.elf_core_dump 46499 0.8111 libc-2.5.so malloc
MYSQLparse是5.x版本中的,在4.x中是YYparse
MYSQLparse() 和 MYSQLlex()是在mysql解析sql语句的时候调用到的。
make_join_statistics()和JOIN::optimize() 是在query optimization(查询优化)阶段调用到的。
正是因为使用了SQL语句,才会有这些额外的负担。
从oprofile的输出可以得到如下结论:
SQL层严重影响到了mysql查询的性能。
与memcached和SQL比起来,mysql要额外做一些工作:
* Parsing SQL statements 解析sql语句
* Opening, locking tables 打开并锁定表
* Making SQL execution plans ???
* Unlocking, closing tables 解锁并关闭表
花荣注:使用mysqli 中的prepared statement API可以避免解析sql语句。
mysql还必须要做大量的并发控制,比如在发送/接收网络数据包的时候,fcntl()就要被调用很多很多次。
Global mutexes :LOCK_open LOCK_thread_count 也被频繁地调用。
所以在oprofile的输出中,排在第二位的是my_pthread_fastmutex_lock()。并且%system占用的CPU相当高(28%)。
其实 mysql开发团队和一些外围的开发团体都了解大量并发控制对性能的影响,
他们在mysql 5.5中已经解决了某些问题。未来的mysql中,%system占用的cpu会越来越少。
但是,%user占用的60%cpu怎样处理呢?
Mutex contentions result in %system increase, not %user increase
即使所有的并发问题都得到处理,估计也很难做到300,000 qps。
或许你听说过HANDLER statement的性能也不错。
可是HANDLER statement需要query parsing,需要open/close table。
不会有太大的帮助。
在完全内存操作的情况下,CPU的效率非常重要
如果只有一小部分数据能够进入内存,那么SQL语句的解析带来的额外负担已经不算什么了。
因为磁盘的IO操作会消耗更长的时间。
在我们的mysql服务器中,内存是大大的,几乎所有的数据都可以放进内存。
SQL层就变成了额外负担,占用了大量的cpu资源。
在线上的应用中,我们要进行大量的PK查询。即使70-80%的查询都是在同一张表上进行的, mysql还是每次都要parse/open/lock/unlock/close,看起来就感觉效率低下。
We needed to execute lots of primary key lookups(i.e. SELECT x FROM t WHERE id=?) or limited range scans. Even though 70-80% of queries were simple PK lookups from the same table (difference was just values in WHERE), every time MySQL had to parse/open/lock/unlock/close, which seemed not efficient for us.
花荣注:难道说mysql中的table_open_cache不是用来减少table open的次数的么。。
NDBAPI
有没有办法在sql层进行优化呢?
http://dev.mysql.com/doc/ndbapi/en/index.html
如果你使用mysql cluster, NDBAPI会是最佳解决方案。
It’s recommended using NDBAPI for frequent access patterns, and using SQL + MySQL + NDB for ad-hoc or infrequent query patterns.
这就是我们想要的:
1 faster access API.
2 sql语句仍然要可用,以处理一些特定的或者复杂的查询。
但是,把innodb转化成ndb可不是一件轻松的事情。
HandlerSocket Plugin
最好的办法是在mysql内部实现一个NoSQL的网络服务。daemon plugin。
它监听在某个端口,接受NoSQL 协议/API的数据包,使用Mysql internal storage engine API直接在innodb数据表上进行操作,并且返回相应的数据。
关于mysql internal st