目前使用MySQL的网站,多半同时使用Memcache作为键值缓存。虽然这样的架构极其流行,有众多的案例,但过于依赖Memcache,无形中让Memcache成为故障的根源:
- Memcache数据一致性的问题:当MySQL数据变化后,如果不能及时有效的清理掉过期的数据,就会造成数据不一致。这在强调即时性的Web2.0时代,不可取。
- Memcache崩溃后的雪崩效应:作为缓存的Memcache一旦崩溃,MySQL很可能在短时间内承受高负载而宕机。据说前段时间新浪微博就遭遇了这样的问题。
注:关于清理过期数据的问题,可以在程序架构上想办法,如果数据操作有统一DAO封装的话,可以利用Observer模式来清理过期数据,非主题内容,资料自查。
面对这些问题,HandlerSocket项目是个不错的解决方案,它通过插件的方式赋予MySQL完整的NoSQL功能,从原理上讲,它跳过MySQL中最耗时的语法解析,查询计划等步骤,直接读取数据,如果内存够大,能装下索引,MySQL的查询效率能提高若干倍!
性能测试:Using MySQL as a NoSQL – A story for exceeding 750,000 qps
因为HandlerSocket的性能足够好,所以就没有必要使用Memcache了,能节省大量的硬件资源,相当低碳!而且HandlerSocket操作的是MySQL放在内存中的索引,没有额外的缓存,所以自然就不存在数据一致性的问题。
安装
如果使用Percona Server版本的MySQL就简单了,因为它已经内置了HandlerSocket支持,不过考虑到其内置的版本不够新,存在一些早已修复的BUG,所以最好采用源代码编译。
注:旧版本HandlerSocket的一些问题可参见:What’s up with HandlerSocket?
官方已经有了一份简单的安装文档,但在我实际安装时,遇到了一些其他未说明的问题,所以这里就把相应的安装过程再写一遍。
首先要确保已经安装了MySQL5.1以上的版本,我用的是Ubuntu操作系统,事先已经用apt安装了MySQL5.1.37,同时还需要相应的mysql_config,如果是Ubuntu的话,可以:
shell> aptitude install libmysqld-dev
注:如果你用的MySQL是从源代码编译的或官方提供的二进制版本,可以略过此步。
接着下载一份和系统MySQL版本一致的MySQL源代码和HandlerSocket源代码:
shell> tar zxf mysql-5.1.37.tar.gz shell> tar zxf ahiguti-HandlerSocket-Plugin-for-MySQL-1.0.6-76-gf5f7443.tar.gz shell> cd ahiguti-HandlerSocket-Plugin-for-MySQL-f5f7443 shell> ./autogen.sh shell> ./configure --with-mysql-source=../mysql-5.1.37 \ --with-mysql-bindir=/usr/bin \ --with-mysql-plugindir=/usr/lib/mysql/plugin
其中的参数含义如下:with-mysql-source表示MySQL源代码目录,with-mysql-bindir表示MySQL二进制可执行文件目录(也就是mysql_config所在目录),with-mysql-plugindir表示MySQL插件目录,如果不清楚这个目录在哪,可以按如下方法查询:
mysql> SHOW VARIABLES LIKE 'plugin%'; +---------------+-----------------------+ | Variable_name | Value | +---------------+-----------------------+ | plugin_dir | /usr/lib/mysql/plugin | +---------------+-----------------------+
运行命令后,如果你使用的是MySQL5.1.37版本的话,会遇到如下错误信息:
MySQL source version does not match MySQL binary version
明明我们的MySQL源代码版本和二进制版本都是5.1.37,为什么还会出现这个错误呢?通过查询HandlerSocket的编译脚本,发现原来它会检索MySQL源代码目录中的VERSION文件,可MySQL5.1.37的源代码目录里不知何故竟然没有这个文件,所以就报错了,既然知道了原因,那我们就照猫画虎做一个VERSION文件放到MySQL源代码目录,内容如下:
MYSQL_VERSION_MAJOR=5 MYSQL_VERSION_MINOR=1 MYSQL_VERSION_PATCH=37 MYSQL_VERSION_EXTRA=
再次运行configure脚本,应该就OK了,把剩下的步骤进行完:
shell> make shell> make install
接着需要配置一下HandlerSocket,编辑MySQL配置文件,加入如下内容:
[mysqld] loose_handlersocket_port = 9998 # the port number to bind to (for read requests) loose_handlersocket_port_wr = 9999 # the port number to bind to (for write requests) loose_handlersocket_threads = 16 # the number of worker threads (for read requests) loose_handlersocket_threads_wr = 1 # the number of worker threads (for write requests) open_files_limit = 65535 # to allow handlersocket accept many concurrent # connections, make open_files_limit as large as # possible.
此外,InnoDB的innodb_buffer_pool_size,或MyISAM的key_buffy_size等关系到缓存索引的选项尽可能设置大一些,这样才能发挥HandlerSocket的潜力。
注:apt包管理下的配置文件一般是/etc/mysql/my.cnf,否则一般是/etc/my.cnf
最后登陆MySQL并激活HandlerSocket插件:
mysql> INSTALL PLUGIN handlersocket soname 'handlersocket.so';
重启一下MySQL服务,如果没有问题,就能在MySQL里看到HandlerSocket的线程了:
mysql> SHOW PROCESSLIST;
也可以通过查询刚配置的端口是否已经被MySQL占用来确认是否安装成功:
shell> lsof -i :9998 shell> lsof -i :9999
完活儿!现在你的MySQL已经具备NoSQL的能力了!
实战
首先创建一个测试用的表:
CREATE TABLE IF NOT EXISTS `test`.`t` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `a` varchar(10) NOT NULL, `b` varchar(10) NOT NULL, PRIMARY KEY (`id`), KEY `a_b` (`a`,`b`) ) ENGINE=InnoDB;
注:理论上HandlerSocket支持MyISAM,InnoDB等各种引擎,不过推荐使用InnoDB。
HandlerSocket的协议非常简单,指令通过TAB分割,一行就是一个请求。本文用到了:
- 打开索引:P <索引标识> <数据库> <表> <索引> <字段>
- 插入数据:<索引标识> ‘+’ <参数个数> <参数1> … <参数N>
- 读取数据:<索引标识> <操作> <参数个数> <参数1> … <参数N> <条数> <偏移>
SQL原型:INSERT INTO test.t (id, a, b) VALUES (1, ‘a1′, ‘b1′), (2, ‘a2′, ‘b2′)
shell> telnet localhost 9999 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. P 1 test t PRIMARY id,a,b 0 1 1 + 3 1 a1 b1 0 1 0 1 + 3 2 a2 b2 0 1 0
注:使用HandlerSocket时,因为没有实际运行SQL,所以Binlog记录的是Row格式。
SQL原型:SELECT id, a, b FROM test.t WHERE id = 1 LIMIT 1
shell> telnet localhost 9999 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. P 1 test t PRIMARY id,a,b 0 1 1 = 1 1 1 0 0 3 1 a1 b1
SQL原型:SELECT id, a, b FROM test.t WHERE id >= 1 LIMIT 2
shell> telnet localhost 9999 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. P 1 test t PRIMARY id,a,b 0 1 1 >= 1 1 2 0 0 3 1 a1 b1 2 a2 b2
SQL原型:SELECT id, a, b FROM test.t WHERE a = ‘a1′ AND b = ‘b1′ LIMIT 1
shell> telnet localhost 9999 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. P 1 test t a_b id,a,b 0 1 1 = 2 a1 b1 1 0 0 3 1 a1 b1
对HandlerSocket一个常见的误解是只能执行PRIMARY类型的KV查询,实际上只要支持索引,一般的简单查询它都能胜任,篇幅所限,这里就不多说了,如果你觉得直接操作telnet有些吃力,也可以使用自己熟悉的客户端来测试,官方文档里有介绍。
注:HandlerSocket作者写了一个不错的PPT可以参考:HandlerSocket plugin for MySQL
记:MySQL5.6提供原生的Memcached API,实际就是KV型NoSQL了,但HandlerSocket并不局限于KV形式,所以仍然有生存空间。
互联网技术发展犹如一列高速运行的火车,下一站:HandlerSocket!大家做好准备吧。
原文:http://huoding.com/2011/04/10/62
下面的是真实的例子,可供参考:
[ root@aslibra www.aslibra.com ]# mysql -S /Data/www.aslibra.com/mysql/mysql.sock -uadmin -ppassword
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 322891
Server version: 5.5.2-m2-log Source distribution
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
#显示当前所有日志
mysql> show master logs;
+------------------+------------+
| Log_name | File_size |
+------------------+------------+
| mysql-bin.000001 | 126 |
| mysql-bin.000002 | 1074328576 |
| mysql-bin.000003 | 918443740 |
| mysql-bin.000004 | 126 |
| mysql-bin.000005 | 126 |
| mysql-bin.000006 | 109880744 |
+------------------+------------+
6 rows in set (0.00 sec)
#删除日志到某一个,不能超过最后一个
mysql> purge master logs to 'mysql-bin.000027';
ERROR 1373 (HY000): Target log not found in binlog index
#删除日志到最后一个即可
mysql> purge master logs to 'mysql-bin.000006';
Query OK, 0 rows affected (3.80 sec)
#看看现在的情况
mysql> show master logs;
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.000006 | 109884395 |
+------------------+-----------+
1 row in set (0.00 sec)
#退出
mysql> exit
做所有的数据库同步需要很多无用的空间,如果仅仅用到其中一个数据库,那就做一个数据库的同步就足够了。
步骤一 主数据库开通replication用户
按需求不一样名称和密码以及IP不同:
GRANT REPLICATION SLAVE ON * . * TO 'slave'@'192.168.1.2' IDENTIFIED BY '***' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0 ;
步骤二 取得当前数据备份,备份mysql库和你要的database2
方式1 从master取得
show master status; //取得当前日志点
//打包文件或者是直接导出数据,比停止服务好的地方就是有一段读数据库的时间
unlock tables; //解锁
分别是 huge large medium 等,还有高压力的InnoDB的参考:
[root@aslibra support-files]# grep -v ^# my-huge.cnf
key_buffer = 384M
table_cache = 512
sort_buffer_size = 2M
read_buffer_size = 2M
read_rnd_buffer_size = 8M
myisam_sort_buffer_size = 64M
thread_cache_size = 8
query_cache_size = 32M
thread_concurrency = 8
希望增加一台只读的从数据库,已经有主从配置。
这里就不必停止主服务器做主从了,从服务器拷贝一份就直接能够处理。
方法:
先停止从服务器的同步 slave stop
这样,从服务器就是处于一个没有更新的且只读的状态,如果没有inodb类型的,那整个数据库文件夹拷贝一份就可以了,我的配置文件在此文件夹里。
拷贝完毕,执行 slave start 继续同步。
启用另外一台:
把拷贝的那份放另外一个机器上运行,能够正常运行,没有太留意问题。
正常状态应该是在主服务器看到slave的进程的状态是:
比较奇怪的是,我有三台slave,只有两个进程看的到,另外一台的进程看不到。
在另外一台上查看状态是
还非常奇怪,一直都是这个状态,明显可以检查出来,数据更新延后了,半天时间过去了也这样。
问题所在:
想起来配置文件没有修改,server-id是一样的,这个可能是问题。
遂修改所有slave服务器为唯一的id,问题解决。
三个slave的进程都在主服务器进程,状态都是
1 sql语句不支持,产生大量错误日志
2 有重复执行的迹象,发生数据重复插入到master,还不确定这问题是否amoeba的问题
3 读写分离会在master出现读进程
第二点,就目前的使用情况而言,amoeba还是很稳定的,只是不太清楚是什么问题,如果重复插入数据的问题不是amoeba产生的,那对于应用而言没有错误发生。
对于第一点和第三点,我觉得是这么一个过程:
1 读写分离时会分析sql语句
2 如果语句属于检查范围的则正常进行读写判断,读操作发送到writePool,写操作发送至readPool,未知的sql语句则发送至defaultPool
3 一般defaultPool和写操作是master,所以在master发现select语句很正常,肯定是判断不出来的语句
sql出现的问题比较多,在log里面有找到以下性质的语句错误记录:
2 EXPLAIN XXX
3 SELECT * FROM xx FORCE INDEX (xx)
4 insert into xx values('\'test\'') 转义的单引号
5 order by rand()
希望amoeba可以正常运行,不产生第二点错误就很满意了。
对于读操作,使用amoeba肯定是不错。
另外,master重启可能导致主从同步会不太稳定,多个从服务器的话比较长的时间才能稳定下来。
比较经常出现“Queueing master event to the relay log”,slave进程都没有持续,可能导致更新不够及时。
@20100105 补充:以上可能是server-id的问题,参考这里
About Amoeba
端透明。具有负载均衡、高可用性、sql过滤、读写分离、可路由相关的query到目标数据库、可并发请
求多台数据库合并结果。
主要解决:
* 降低 数据切分带来的复杂多数据库结构
* 提供切分规则并降低 数据切分规则 给应用带来的影响
* 降低db 与客户端的连接数
* 读写分离
* 制定一种规则可支持DB线性扩容
目前在amoeba 框架上面已经实现了 amoeba for mysql.
找寻到amoeba是出于想要做读写分离,目前可以有三种解决方式:
1 程序修改mysql操作类
可以参考PHP实现的Mysql读写分离,阿权开始的本项目,以php程序解决此需求。
优点:直接和数据库通信,简单快捷的读写分离和随机的方式实现的负载均衡,权限独立分配
缺点:自己维护更新,增减服务器在代码处理
2 amoeba
参考官网:http://amoeba.meidusa.com/
优点:直接实现读写分离和负载均衡,不用修改代码,有很灵活的数据解决方案
缺点:自己分配账户,和后端数据库权限管理独立,权限处理不够灵活
3 mysql-proxy
参考 mysql-proxy。
优点:直接实现读写分离和负载均衡,不用修改代码,master和slave用一样的帐号
缺点:字符集问题,lua语言编程,还只是alpha版本,时间消耗有点高
如果你不能安装软件来解决读写分离,那可以尝试阿权的项目解决思路。
如果你可以安装软件,那amoeba是不错的,mysql-proxy不太建议,目前只有alpha版本,效率还不太理想,amoeba目前在阿里巴巴是内部项目,正在生产环境使用的。
amoeba的安装使用
1 安装java环境,需要Java SE 1.5 或以上
2 配置xml文件
下载地址: http://www.sf.net/projects/amoeba
解压就可以使用的,顺便说一下,打包的习惯似乎不是太好,最好解压后是自己的文件夹
运行很简单 bin/amoeba 即可,后台运行 bin/amoeba &
如果没有配置JAVA_HOME,则会有如下提示:
Error: JAVA_HOME environment variable is not set.
如果你是比1.5低,比如1.4的,运行会有错误提示:
Exception in thread "main" java.lang.UnsupportedClassVersionError: com/meidusa/amoeba/mysql/server/MysqlProxyServer (Unsupported major.minor version 49.0)
at java.lang.ClassLoader.defineClass0(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:539)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:123)
.....
安装配置java环境对于不玩java的人不太熟悉,我也不是行家,把自己的配置过程作一下分享:
首先根据你的操作系统下载相应的文件,比如jdk-1.5.0,文件下载地址:
http://java.sun.com/javase/downloads/index.jsp
下载回来的是bin文件,加执行属性,然后执行后就能有一个rpm文件,然后安装即可:
Preparing... ########################################### [100%]
package jdk-1.5.0_15-fcs is already installed
配置JAVA_HOME变量:
[root@aslibra java]# ll
total 16
drwxr-xr-x 8 root root 4096 Apr 27 17:50 j2sdk1.4.2_15
lrwxrwxrwx 1 root root 13 Apr 27 17:52 jdk -> j2sdk1.4.2_15
drwxr-xr-x 9 root root 4096 May 13 09:14 jdk1.5.0_15
[root@aslibra java]# rm jdk
rm: remove symbolic link `jdk'? y
[root@aslibra java]# ln -s jdk1.5.0_15/ jdk
[root@aslibra java]# ll
total 16
drwxr-xr-x 8 root root 4096 May 13 09:20 j2sdk1.4.2_15
lrwxrwxrwx 1 root root 12 May 13 09:21 jdk -> jdk1.5.0_15/
drwxr-xr-x 9 root root 4096 May 13 09:14 jdk1.5.0_15
可以编辑 /etc/profile以便启动时变量生效,末尾加上
JAVA_HOME=/usr/java/jdk
PATH=$PATH:JAVA_HOME/bin
export JAVA_HOME PATH
依次运行此三句,配置好xml后,即可立刻使用amoeba:
log4j:WARN log4j config load completed from file:/Data/apps/amoeba-mysql/conf/log4j.xml
log4j:WARN ip access config load completed from file:/Data/apps/amoeba-mysql/conf/access_list.conf
2009-05-13 09:22:04,306 INFO net.ServerableConnectionManager - Server listening on /192.168.1.5:9306.
配置amoeba:
conf/amoeba.xml 配置mysql数据库,简单说明一下:
1 server节点定义amoeba为接受client访问的数据库,可以当作是mysql看待的,用户名和密码是访问时使用的,这个似乎不能定义多个用户名密码,也就是只有一个权限控制,这个对于多应用似乎不大方便。
2 dbServerList里面可以定义很多实际的mysql数据库,增加dbServer节点即可,这里的用户名密码是作为amoeba操作数据库使用的,要有足够权限。dbServer可以是虚拟的,比如要做负载均衡时可用定义多个数据库归属到此虚拟数据库。
3 queryRouter节点定义读写的分配情况,也就是读写该发往那个dbServer。
读写分离的配置示例片段:
<dbServer name="master">
....
</dbServer>
<dbServer name="slave1">
....
</dbServer>
<dbServer name="slave2">
....
</dbServer>
<dbServer name="slave3">
....
</dbServer>
<dbServer name="multiPool" virtual="true">
<poolConfig class="com.meidusa.amoeba.server.MultipleServerPool">
<property name="loadbalance">1</property>
<property name="poolNames">slave1,slave2,slave3</property>
</poolConfig>
</dbServer>
</dbServerList>
<queryRouter class="com.meidusa.amoeba.mysql.parser.MysqlQueryRouter">
<property name="LRUMapSize">1500</property>
<property name="defaultPool">master</property>
<property name="writePool">master</property>
<property name="readPool">multiPool</property>
</queryRouter>
另外一个需要修改一下的是 conf/log4j.xml,定义日志内容:
日志是非常消耗系统性能的,在没有必要的情况下可以不使用debug。
参考:关于amoeba 性能测试注意的几点
不算复杂的处理就完成了amoeba的配置和使用了,更加复杂的应用请参考官网介绍资料:
amoeba 中文文档下载地址:http://amoeba.meidusa.com/amoeba.pdf





