存档

2010年12月 的存档

HandlerSocket调研

2010年12月11日 12 条评论

概述

背景

在前MySQL核心开发人员Yoshinori Matsunobu宣传handlerSocket之后,最近这个插件颇受关注 。它的核心思想很简单,在profiling后发现对于简单的主键查询,SQL层的消耗很大。在数据集较小,能够在内存中存放的情况下(此时随机的Read IO可以忽略),SQL层就成了最大的瓶颈。为方便不能爬墙的同学,RT一下原文给出的剖分结果:

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

可以看出,简单的SQL查询方式下,有很大比例的时间消耗在SQL解析、Query Plan、表锁等SQL层上。因此,如果跳过SQL层,直接与存储引擎进行交互,就可以获取很大程度的性能提升。

基于这一思想,他们的团队开发了HandlerSocket。测试结果显示,单机查询性能能够到达75W+(100w条数据,做纯内存主键查询),这个数字意味着已经超过了现在绝大多数KV存储系统、甚至缓存系统的性能。

MySQL的Vadim觉得这玩艺儿挺靠谱(enjoyed),也对它进行了测试,并在MySQL Performance Blog上给出了测试的结果,这个测试关注了数据量大到需要换入换出时handlersocket的性能表现。结论与预期的相符:当数据在内存能装下时,性能稳定在60W+ rps的水平,但当数据大到一定级别时,性能开始下降。此时主要的瓶颈就在于IO,像FusionIO这样强悍的硬件,还可以支撑到40W+,而普通的RAID10就已经惨不忍睹。 也就是说数据量大拼的就是硬件IO性能,此时HandlerSocket在SQL层节省的CPU消耗,在巨大的IO成本前不值一提。

插件结构

再RT一下Yoshinori给出的结构图:

image

图一:HandlerSocket结构 (来源于slidershare的PPT)

mysql_handlersocket

图二: HandlerSocket结构 (来源于原文)

 

这两张图大同小异,但主旨都是在正常的SQL解析层外,HandlerSocket为我们开了一条后门,直接通过MySQL的HandlerInterface与存储引擎打交道。第二张结构图更详细,可明显看出HandlerSocket要做的事情比正常的SQL少很多。

特性

在原文作者列出了HandlerSocket的一些特性,整理了一下相对重要的,再加上自己的一些粗浅的理解:

  • 高效简洁的网络框架
    • 在Linux系统上采用epoll的方式驱动,否则采用poll的方式。
    • 单客户端连接的成本很低,PHP等可以方便的使用短连接访问,而不用再担心并发连接数问题(ps. fcicq怀疑新浪微博就是这么杯具的)。
  • 高性能。除了直接访问引擎接口外,HS还做了一些工作用来提升性能
    • 很简洁的网络接口,协议包的冗余数据很少,减少网络带宽占用。
    • 自动Group客户端请求
      • 用过MySQL的同学应该都知道批量提交/批量查询的技巧,用来提升性能
      • HandlerSocket往前走了一步,在Server层面为我们做类似的优化,自动Group尽可能多的请求,一次性提交
    • 开放了一个“只能”进行读操作的端口(图二中的9998)
      • 两个端口被称之为ReadPort和WritePort,但不要被名字迷惑。虽然Read端口只能读,但Write Port一样可以进行Read操作
      • 读请求虽然也是个事务,但纯读可以节省很多成本,比如Transaction Commit,以最大限度提高性能
      • 在稍后的测试中,我们将对这两个端口的读性能进行测试
  • HandlerSocket支持多种请求
    • 支持主键查找、列索引查找、范围查询、LIMIT
    • 支持INSERT (注意:无法返回生成的key)
    • 支持UPDATE
    • 支持DELETE
  • 仍然支持SQL查询 ,对于复杂查询,仍然可以走普通的SQL接口进行访问
  • 数据由相对成熟的数据库引擎(InnoDB)管理,崩溃安全性良好,也可以快速恢复
  • 不需要重新编译MySQL
  • 支持Row-Based的主从同步
    • 由于它跳过了SQL层,所以只能选择存储引擎的行级数据同步,而不能做Statement级别的同步(行级同步在mysql5.1版本引入的,这就是HandlerSocket要求mysql版本5.1+的原因微笑)
    • binlog_format是session相关的变量,HandlerSocket会调用Interface,设置Row-Based同步。
  • 运维方面简单,现有大量的MySQL运维工具和经验可以直接使用

源码分析

作为一个较新的开源项目,HandlerSocket的文档比较薄弱。幸好它的代码还是很简单的,有什么疑问翻一下代码基本都能解决。这里就不展开很细致的代码分析,主要分析一下代码层面重要的几个点。

插件实现

图一告诉我们,HandlerSocket和SQL Layer在同一层,但实际上这个地方有点小trick。它以daemon plugin的形式的,在这个意义上说,它和InnoDB/MyISAM等引擎插件在同一层;但在daemon_handlersocket_init里,就自己listen端口、起worker线程、接收请求、直接与存储引擎交互。

没有插件开发经验的同学,理解这个trick可能会稍有些疑惑:它是如何被调度的?它又是如何直接访问其他存储引擎的?

  • 插件引擎都会有一套接口规范,具体实现的插件都必须遵守这个接口规范,以函数指针或者类对象继承方式由插件引擎调用。而接口规范一般都有init接口用于插件初始化。HandlerSocket就利用了这一特性,Init时开了一个后门( 代码文件:handlersocket.cpp )
  • MySQL有一层Handler层(图1中的HandlerInterface),它直接与各个存储引擎交互,并负责XA事务的两阶段提交,HandlerSocket调用的就是HandlerInterface的ha_update_row、ha_delete_row、ha_write_row或index_read_map等接口进行CRUD操作。这也是它名字的由来微笑(代码文件 database.cpp)

工作流程

worker thread的流程清晰明了,总体流程如下:

image

事务模型

工作流程的图示中可以看出,在一次epoll_wait返回的请求,将一并commit,这也是HandlerSocket的基本事务模型:

  • 写线程以一个epoll_wait收到的“所有”读写请求作为一个事务
  • 事务隔离级别也没有特殊之处,各存储引擎按照配置进行
  • 由HandlerInterface管理XA事务,对事务表和非事务表的提交,与正常SQL处理也相同
  • 锁冲突也是由各存储引擎处理,MySQL用行锁,InnoDB用行级锁。这里需要注意:如果在3306端口进行了Lock,HandlerSocket一样会阻塞等待。
  • 也不会影响到MVCC

协议

HandlerSocket使用了自定义协议进行交互。具体协议有文档说明,参考源码目录docs-en/protocol.en.txt。协议这里就不详细展开,只提一下基本语法:

  • 一个命令一行,采用\n分隔,行内每项数据用\t分隔
  • 由于\t\n在协议中有特殊含义,如果数据含有\t、\n,就需要进行转义(转义规则设计的有点奇怪,有哪位同学知晓设计思想,欢迎赐教)
    • [0x10-0xff]不转义
    • [0x00-0x0f]表示为两字节: [0x01] [0x40+value]
  • NULL用\0表示,以区别长度为0的字符串

调研测试

侧重点

  • 插入性能
    • HandlerSocket与SQL性能对比
    • Group提交对时延的影响
  • 读取性能
    • HandlerSocket与SQL性能对比
    • Read Port与Write Port性能对比
  • 主从同步

测试环境

  • 硬件

两台DELL PowerEdge 2950,4核Intel Xeon 5510 @2.66G, 16G内存

  • 软件

Red Hat Enterprise Linux AS release 4 (Nahant Update 3)

mysql 5.1.53 Linux-generic-source

HandlerSocket a485973

  • 软件配置

MySQL:

innodb_buffer_pool_size = 8G
innodb_flush_log_at_trx_commit = 2
innodb_thread_concurrency = 16
innodb_log_buffer_size = 8M
innodb_log_file_size = 256M
innodb_max_dirty_pages_pct = 90

HandlerSocket:

loose_handlersocket_port = 9998
loose_handlersocket_port_wr = 9999
loose_handlersocket_threads = 4
loose_handlersocket_threads_wr = 1
open_files_limit = 65535
  • 表格式
CREATE TABLE user (
  user_id INT UNSIGNED PRIMARY KEY,
  user_name VARCHAR(50),
  user_email VARCHAR(255),
  created DATETIME
) ENGINE=InnoDB;
  • 采用tcprstat测量响应时间

测试

写入性能

SQL
seq 1000000 | sed 's/\(.*\)/INSERT INTO user set user_id=\1, user_name=\1, user_email=\1;/' > handlersocket.sql
time mysql -D test < handlersocket.sql

image

HandlerSocket

扩展hstest程序,增加测试用例,插入与SQL相同数据。

image

image

读性能

SQL
关闭qcache
mysqlslap --query="select user_name from user where user_id=1" --number-of-queries=10000000 --concurrency=30 --host=HOST --port=3306

image

image

HandlerSocket Read Port
./hstest test=11 tablesize=1000000  host=10.26.53.34 hsport=9998 num=10000000 num_threads=100 timelimit=10 

image

image

HandlerSocket WritePort

在这里,我们需要修改loose_handlersocket_threads_wr,将WritePort的工作线程数为4,保持与ReadPort一致,之后再运行hstest。

image

image

结论

简单整理分析一下:

image

  • 写性能约为SQL的3.74倍
  • 读性能约为SQL的3.83倍,达到20w左右。测试结果离官方宣称的75w+还有一定距离。应该是测试环境的问题:
    • 硬件
    • mysql版本使用linux-generic版本,未进行configue优化。这个原因可能性较大,因为profile发现锁开销很大,纯读不应该出现这种情况。另外Yoshinori的测试profile结果也显示他们版本的瓶颈在网络层。
  • Group提交方式,造成了一定程度的服务时延,平均时延较SQL方式大了1倍左右

总体来看,HandlerSocket有着很不错的性能表现。在以下case应该有不错的应用前景:

  • 缓存系统:性能已经接近甚至超过了memcache,还支持固化、崩溃恢复;
  • 内存数据库:handlersocket直接用存储引擎做后端,当后端使用InnoDB时,可以理解成一个B树组织、支持Adaptive Hash的内存数据库。虽然几十万级别的数字,对于内存数据库来说,可能还有挖掘潜力,但毕竟这些存储引擎久经考验,数据安全性值得依赖,而且还是免费的。在小数据量高性能存储的场景,HandlerSocket是一个不错的代替方案。
分类: MySQL, 存储 标签:

Godaddy上dokuwiki安全策略

2010年12月4日 没有评论

在GoDaddy上架了个dokuwiki做简单的pkm。在管理员登录后,dokuwiki总有一个安全提示,说dokuwiki的安全设定有问题。之前没去折腾它,一直忍着大红叉,直到今天无意打开了指导文章,按照上面的提示,访问了一下url: http://xiaoy.info/pkm/dokuwiki/data/pages/wiki/dokuwiki.txt,居然很顺利的就把wiki内容取出来了(别试了,现在已经不行了微笑),按图索骥就可得到所有wiki,就算设定了private也照样可以拉到。赶紧看解决方案,官方给出两个方法:

  1. 通过WebServer配置,禁用关键目录(data/conf)的访问
  2. 将关键目录从htdocs移走

杯具的是,这两个方法对godaddy免费windows主机都无效。它上面跑的是IIS,而IIS想禁用关键目录访问,必须有IIS控制权限,免费主机当然是没有的。

而第2个方法,godaddy为每个用户只提供了htdocs目录,无法把文件放置于其他地方。

难道就没有别的解决方案了么?分析一下现在的目标是:让用户无法直接访问到关键目录。官方两种方法为了达到这个目的,采用了webserver配置或者htdocs的方法,但达到这个目的只有这两个方法么?显然答案是no.在godaddy上,可以用很简单的基于IIS的方案:

修改web.config,用rule匹配的方式,禁用掉data目录和conf目录的访问:

<rule name="secure">
	<match url="^pkm/dokuwiki/(data|conf|bin|inc)/?(.*)?$" />
	<action type="CustomResponse" statusCode="403" statusReason="Forbidded" statusDescription="Forbidden" />
</rule>

当识别到关键目录的直接访问时,直接返回403错误,这样就很简单明了的解决了问题微笑

 

参考: http://learn.iis.net/page.aspx/557/translate-htaccess-content-to-iis-webconfig/

分类: 建站 标签: , ,

OPROFILE安装试用

2010年12月3日 没有评论

OPROFILE是一个开源的profiling工具,类似于vtune和gprof。但不需要像gprof一样,必须优雅退出才可以剖分。今天在看HandlerSocket文章时,想重复一下作者的oprofile结果,因此,安装试用了一下,遇到一些问题,过程记录如下。

1. 连到服务器开发机上,发现已经安装了oprofile。尝试初始化: sudo opcontrol –init 出现错误:kernel doesn’t support oprofile. 看着很吓人,以为需要重新编译内核。但仔细看了看介绍,说2.6的内核已经以module的方式支持oprofile。因此,只需要sudo modprobe oprofile一下,如果正常就可以继续,否则就说明没有安装这个module,需要重编内核,在menuconfig时选择是否添加。。

如果modprobe正确,仍出现这个错误,那说明opcontrol没有对应的kernel driver. 解决方案是重新编译安装oprofile。

2. 安装

wget http://prdownloads.sourceforge.net/oprofile/oprofile-0.9.6.tar.gz
tar xvfz oprofile.*.gz
./configure --prefix=/home/work/software/output/ --with-kernel-support

出现

/usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/3.4.4/../../../../lib64/libbfd.a(archures.o): relocation R_X86_64_32 against `a local symbol’ can not be used when making a shared object; recompile with –fPIC

改为:

./configure --prefix=/home/work/software/output/ --with-kernel-support --enable-shared=no

再make

wps_clip_image-9659

服务器用的是64位的系统,而它默认依赖到32位的库.因此,导出LDFLAGS=-L/usr/lib64后,make clean后再make,就成功通过了.

wps_clip_image-16547

最后还出个waring,提示要添加用户,如果不调试JIT,直接忽略,不用添加用户。

3. 这就可以开始使用oprofile了,不过需要注意的是,需要有root权限才可以运行,请向系统管理员索要sudo权限。

4. 对mysqld进行profile为例:

sudo opcontrol --reset
sudo opcontrol --separate=lib --no-vmlinux --start --image=/home/software/output/libexec/mysqld
在其他机器起压力,压力停止后再进行后续操作
sudo opcontrol --dump
sudo opcontrol --shutdown

opreport -l /home/software/output/libexec/mysqld
opannotate -s /home/software/output/libexec/mysqld 

参考:http://oprofile.sourceforge.net/doc/install.html

分类: Linux 标签: