NoSQL小故事单服务器每秒75万次查询
NoSQL 小故事:单服务器如何应付每秒75万次查询【51CTO 经典译文】大多数大规模Web 应用程序都使用MySQL Memcached架构,其中许多应用也同时使用了NoSQL 数据库,如Toky
NoSQL 小故事:单服务器如何应付每秒75万次查询
【51CTO 经典译文】大多数大规模Web 应用程序都使用MySQL Memcached架构,其中许多应用也同时使用了NoSQL 数据库,如TokyoCabinet/Tyrant,也有一些人全部放弃MySQL ,转投NoSQL 的怀抱,曾经有人将这称为NoSQL 运动,因为NoSQL 数据库在处理一些简单访问模式,如主键查找时,比MySQL 的表现更好,大多数Web 应用程序的查询都很简单,因此这看上去是一个很合理的决定。
和许多其它大规模网站一样,我们的DeNA (我于2010年8月离开Oracle ,加盟了日本最大的社交游戏平台提供商DeNA )多年来都存在类似的问题,但我们得出了不同的结论,最终我们全部使用了MySQL ,当然一如既往地使用Memcached 作为前端缓存(如预处理的HTML ,计数/摘要信息),但我们没有使用
Memcached 缓存数据行,我们也没有使用NoSQL ,因为我们从MySQL 获得的性能比其它NoSQL 产品更好,在我们的基准测试中,我们在一台普通的MySQL/InnoDB 5.1服务器上获得了750000 QPS的成绩,在生产环境中的性能更优秀。(QPS 每秒查询率,每秒查询率QPS 是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准,在因特网上,作为域名系统服务器的机器的性能经常用每秒查询率来衡量。) 也许你不相信这个成绩,但我说的是真的,在这篇文章中,我将分享一下我们是如何做到的。
SQL 主键查询真得能很快吗?
每秒你可以运行多少次主键查询?DeNA 的应用程序需要执行大量的主键查询,如通过用户id 获取用户信息,通过日记id 获取日志信息,Memcached 和NoSQL 都能很好地适应这种需求,当你运行简单的多线程“memcached get”基准测试时,每秒大约可以执行400000 次get 操作,即使Memcached 客户端位于远程服务器上,当我使用最新的libmemcached 和memcached 测试时,在一台2.5GHz 8核Nehalem 处理器,四个Broadcom 千兆以太网卡的服务器上,测试成绩是每秒执行420000次get 操作。
MySQL 执行主键查询需要多长时间?通过基准测试很容易找到答案,只需要从sysbench ,super-smack 和mysqlslap 等运行并行查询即可。
1. [matsunobu@host ~]$ mysqlslap --query="select user_name,..
,2. from test.user where user_id=1"
3. --number-of-queries=10000000 --concurrency=30 --host=xxx –uroot
你可以使用下面的命令检查每秒读取了多少InnoDB 行:
1. [matsunobu@host ~]$ mysqladmin extended-status -i 1 -r -uroot
2. | grep -e "Com_select"
3. ...
4. | Com_select | 107069 |
5. | Com_select | 108873 |
6. | Com_select | 108921 |
7. | Com_select | 109511 |
,8. | Com_select | 108084 |
9. | Com_select | 108483 |
10.
11.
每秒有100000 次查询似乎还不错,但却远远低于Memcached 的结果,MySQL 实际上做了些什么?从vmstat 输出可以看出,user和system都很高。 | Com_select | 108115 | ...
1. [matsunobu@host ~]$ vmstat 1
2. r b swpd free buff cache in cs us sy id wast
3. 23 0 0 963004 224216 29937708 58242 163470 59 28 12 0 0
4. 24 0 0 963312 224216 29937708 57725 164855 59 28 13 0 0
,5. 19 0 0 963232 224216 29937708 58127 164196 60 28 12 0 0
6. 16 0 0 963260 224216 29937708 58021 165275 60 28 12 0 0
7. 20 0 0 963308 224216 29937708 57865 165041 60 28 12 0 0
Oprofile 输出显示了更多关于CPU 资源消耗的情况。
1. samples app name symbol name
2. 259130 4.5199 mysqldMYSQLparse(void*)
3. 196841 3.4334 mysqldmy_pthread_fastmutex_lock
4. 106439 1.8566 libc-2.5.so _int_malloc
5. 94583 1.6498 bnx2 /bnx2
6. 84550 1.4748 ha_innodb_plugin.so.0.0.0 ut_delay
,7. 67945 1.1851 mysqld _ZL20make_join_statistics
8. P4JOINP10TABLE_LISTP4ItemP16st_dynamic_array
9. 63435 1.1065 mysqld JOIN::optimize()
10.
11.
12.
13.
14.
15.
16.
46499 0.8111 libc-2.5.so malloc 46957 0.8190 vmlinux .text.elf_core_dump 47518 0.8288 libc-2.5.so memcpy 49602 0.8652 ha_innodb_plugin.so.0.0.0 row_search_for_mysql 50833 0.8867 libpthread-2.5.so pthread_mutex_trylock 55054 0.9603 mysqldMYSQLlex(void*, void*) 55825 0.9737 vmlinuxwakeup_stack_begin
,在SQL 解析阶段调用了MYSQLparse()和MYSQLlex(),在查询优化阶段调用了make_join_statistics()和JOIN::optimize(),这些都是SQL 开销,很明显,性能下降主要是由SQL 层,而不是InnoDB (存储)层造成的,MySQL 做了很多Memcached/NoSQL不需要做的事情,如:
1. λ解析SQL 语句
2. λ打开,锁住表
3. λ创建SQL 执行计划
4. λ解锁
5. λ关闭表
MySQL 也做了许多并发控制,例如,在发送/接收网络数据包时多次调用了fcntl(),全局互斥,如LOCK_open,LOCK_thread_count很频繁地创建/释放,这就是为什么oprofile 输出中
my_pthread_fastmutex_lock()排名第二,system也不小的原因。
MySQL 开发团队和外部社区都知道并发问题,有些问题在5.5中已经得到解决,我很高兴地看到,大量的修复工作已经完成,但同样重要的是user也达到了60,互斥竞争导致system上升,而不是user上升。虽然MySQL 中的所有互斥问题都得到了解决,但不要指望每秒超过30万次查询。
,你可能听说过HANDLER 语句,遗憾的是,HANDLER 语句对提高吞吐量并不会有太多帮助,因为查询解析,打开/关闭表等操作仍然是需要的。
CPU 效率对内存中的工作负载非常重要
如果内存中没有合适的活动数据,SQL 开销相对来说可以忽略不计,很简单,因为磁盘I/O成本是非常高的,在这种情况下,我们不需要太关心SQL 成本。
但在我们的热点MySQL 服务器上,几乎所有数据都装入到内存中了,它们完全变成CPU 限制,分析结果和上面类似:SQL 层消耗了大部分资源。我们需要执行大量的主键查询(如SELECT x FROM t WHERE id=?)或限制范围的扫描,即使70-80的查询是对相同表的简单主键查询(不同的只是Where 子句中的值),每次MySQL 都要解析/打开/锁住/解锁/关闭,这对我们来说效率是很低的。
你听说过NDBAPI 吗?
在MySQL 的SQL 层有什么好的解决办法可以减少CPU 资源争用吗?如果你使用的是MySQL 集群,NDBAPI 可能是最好的解决方案,我在MySQL/Sun/Oracle担任顾问时,我看到许多客户对SQL 节点
NDB的性能表现很失望,当使用NDBAPI 客户端性能提高N 倍后,他们高兴极了,你可以在MySQL 集群中同时使用NDBAPI 和SQL ,建议频繁访问模式使用NDBAPI ,即席查询或非频繁模式使用
SQL MySQL NDB。
这正是我们想要的,我们希望更快速地访问API ,我们也希望对即席查询或复杂查询使用SQL ,但DeNA 使用的是InnoDB ,和许多其它Web 服务一样,切换到NDB 不是小事,嵌入式InnoDB 不支持SQL 也不没有网络接口,因此它不适合我们。
HandlerSocket 插件,一个懂NoSQL 网络协议的MySQL 插件
我们认为最好的办法是在MySQL 内部实现一个NoSQL 网络服务器,也就是说,写一个网络服务器作为MySQL 插件(守护进程插件)监听指定端口,接受NoSQL 协议/API,然后使用MySQL 内部存储引擎API 直接访问InnoDB 。这个方法和NDBAPI 类似,但它可以和InnoDB 交互,这个概念最初是由Kazuho Oku去年在Cybozu 实验室提出并创建了原型,他编写了使用memcached 协议的MyCached UDF,我的同事Akira Higuchi实现了另一个插件:HandlerSocket ,下图显示了HandlerSocket 可以做的事情。
,
图 1 Hanldersocket是什么?
Hanldersocket 是一个MySQL 守护进程插件,它让应用程序可以将MySQL 当NoSQL 使,
Hanldersocket 的主要目的是与存储引擎,如InnoDB 交互,而不需要SQL 相关的开销。访问MySQL 表时,Hanldersocket 仍然需要打开和关闭表,但不是每次访问都要求打开和关闭,因此减少了互斥争夺,极大地提高了系统性能,当流量变小时,Hanldersocket 会关闭表,因此它永远不会阻止管理命令(DDL )。 它和使用MySQL Memcached有什么不同?比较图1和图2,我想你会发现很多差异的,图2显示了典型的Memcached 和MySQL 用法,Memcached 用于缓存数据库记录,这是因为Memcached 的get 操作比MySQL 的内存中/磁盘上的主键查询要快很多,如果HandlerSocket 的查询速度和Memcached 一样快,我们就不用Memcached 缓存记录了。
,
图 2 MySQL Memcached的常见架构模式
使用HandlerSocket
举一个例子,假设有一个“user ”表,我们需要通过user_id获取用户信息。
1. CREATE TABLE user (
2. user_id INT UNSIGNED PRIMARY KEY,
3. user_name VARCHAR(50),
,4. user_email VARCHAR(255),
5. created DATETIME
6. ) ENGINE=InnoDB;
在MySQL 中,可以通过SELECT 语句获取用户信息。
1. mysql> SELECT user_name, user_email, created FROM user WHERE user_id=101;
2. --------------- ----------------------- ---------------------
3. | user_name | user_email | created |
4. --------------- ----------------------- ---------------------
5. | Yukari Takeba | yukari.takeba@dena.jp | 2010-02-03 11:22:33 |