分类: NoSQL

Redis 常见的性能问题和解决方法

没有评论

2012 年 07 月 21 日 at 下午 10:22分类:NoSQL

1.Master写内存快照
save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。
2.Master AOF持久化
如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。
3.Master调用BGREWRITEAOF
Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。
下面是我的一个实际项目的情况,大概情况是这样的:一个Master,4个Slave,没有Sharding机制,仅是读写分离,Master负责写入操作和AOF日志备份,AOF文件大概5G,Slave负责读操作,当Master调用BGREWRITEAOF时,Master和Slave负载会突然陡增,Master的写入请求基本上都不响应了,持续了大概5分钟,Slave的读请求过也半无法及时响应,Master和Slave的服务器负载图如下:
Master Server load:

Slave server load:

上面的情况本来不会也不应该发生的,是因为以前Master的这个机器是Slave,在上面有一个shell定时任务在每天的上午10点调用BGREWRITEAOF重写AOF文件,后来由于Master机器down了,就把备份的这个Slave切成Master了,但是这个定时任务忘记删除了,就导致了上面悲剧情况的发生,原因还是找了几天才找到的。
将no-appendfsync-on-rewrite的配置设为yes可以缓解这个问题,设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入。最好是不开启Master的AOF备份功能。
4.Redis主从复制的性能问题
第一次Slave向Master同步的实现是:Slave向Master发出同步请求,Master先dump出rdb文件,然后将rdb文件全量传输给slave,然后Master把缓存的命令转发给Slave,初次同步完成。第二次以及以后的同步实现是:Master将变量的快照直接实时依次发送给各个Slave。不管什么原因导致Slave和Master断开重连都会重复以上过程。Redis的主从复制是建立在内存快照的持久化基础上,只要有Slave就一定会有内存快照发生。虽然Redis宣称主从复制无阻塞,但由于Redis使用单线程服务,如果Master快照文件比较大,那么第一次全量传输会耗费比较长时间,且文件传输过程中Master可能无法提供服务,也就是说服务会中断,对于关键服务,这个后果也是很可怕的。
以上1.2.3.4根本问题的原因都离不开系统io瓶颈问题,也就是硬盘读写速度不够快,主进程 fsync()/write() 操作被阻塞。
5.单点故障问题
由于目前Redis的主从复制还不够成熟,所以存在明显的单点故障问题,这个目前只能自己做方案解决,如:主动复制,Proxy实现Slave对Master的替换等,这个也是Redis作者目前比较优先的任务之一,作者的解决方案思路简单优雅,详情可见 Redis Sentinel design draft http://redis.io/topics/sentinel-spec。
总结
Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化。
如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。
为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。
尽量避免在压力较大的主库上增加从库
为了Master的稳定性,主从复制不要用图状结构,用单向链表结构更稳定,即主从关系为:Master<–Slave1<–Slave2<–Slave3…….,这样的结构也方便解决单点故障问题,实现Slave对Master的替换,也即,如果Master挂了,可以立马启用Slave1做Master,其他不变。 本文转载自:http://blog.nosqlfan.com/html/4077.html

利用keepalived构建高可用MySQL-HA

没有评论

2012 年 03 月 29 日 at 下午 8:31分类:MySQL | NoSQL

本来是找相关Redis的HA方案的,结果找到这篇文章,发现很不错,记录下,方便下次查找

地址:点击查看

上篇文章说配置Redis的主从问题,但我现在想要配置Redis的master-master模式,然后借用keepalived来实现故障自动转移的功能,
但是配置了Redis的相关参数之后,根本就不能达到我想要的目的,不知道是什么原因,看了网上很多的文章,都出现了类似的情况。估计是
Redis现在还不支持这个功能吧 期待高手。

参考资料:
1、http://blog.csdn.net/doraeimo/article/details/7029832
2、http://baike.baidu.com/view/4175422.htm

redis 分布式,主从同步

一条评论

2012 年 03 月 27 日 at 上午 9:37分类:NoSQL

redis和memcache比较像的,memcache可以实现服务器的集群,redis肯定也是可以的。下面在一台机,实现redis主从复制。
1,copy一下redis.conf,生成一个从机的配置

cp /usr/local/redis/redis.conf /usr/local/redis/redis_slave.conf

2,修改主服务器的配置redis.conf

bind 127.0.0.1

3,修改从服务器的配置redis_slave.conf

pidfile /usr/local/redis/var/redis_slave.pid

port 6380

bind 127.0.0.1

logfile /usr/local/redis/var/redis_slave.log

dbfilename dump_slave.rdb

slaveof 127.0.0.1 6379

4,启动主服务器,从服务器

redis-server /usr/local/redis/redis.conf

redis-server /usr/local/redis/redis_slave.conf

查看一下,redis.log文件会有以下内容

[3774] 27 Jun 12:53:32 * The server is now ready to accept connections on port 10002

[3774] 27 Jun 12:53:32 - 0 clients connected (0 slaves), 539568 bytes in use

[3774] 27 Jun 12:53:32 * Connecting to MASTER...

[3772] 27 Jun 12:53:32 - Accepted 127.0.0.1:33237

[3772] 27 Jun 12:53:32 * Slave ask for synchronization

[3772] 27 Jun 12:53:32 * Starting BGSAVE for SYNC

[3772] 27 Jun 12:53:32 * Background saving started by pid 3775

[3774] 27 Jun 12:53:32 * MASTER <-> SLAVE sync started: SYNC sent

[3775] 27 Jun 12:53:32 * DB saved on disk

[3772] 27 Jun 12:53:33 * Background saving terminated with success

[3772] 27 Jun 12:53:33 * Synchronization with slave succeeded

[3774] 27 Jun 12:53:33 * MASTER <-> SLAVE sync: receiving 333 bytes from master

[3774] 27 Jun 12:53:33 * MASTER <-> SLAVE sync: Loading DB in memory

[3774] 27 Jun 12:53:33 * MASTER <-> SLAVE sync: Finished with success

查看一下

root@ubuntu:/usr/local/redis# ps -e|grep redis

3774 ? 00:00:00 redis-server

4148 ? 00:00:00 redis-server

5,测试结果

//连接主服务器
[root@localhost var]# redis-cli -h 127.0.0.1 -p 6379
redis 127.0.0.1:6379> set fbbin binbin
OK
redis 127.0.0.1:6379> get fbbin
"binbin"
redis 127.0.0.1:6379> exit
========================================
//连接从服务器
[root@localhost var]# redis-cli -h 127.0.0.1 -p 6380
redis 127.0.0.1:6380> get fbbin
"binbin"
redis 127.0.0.1:6380> exit

OVER

windows 安装 mongodb 并添加到服务列表中

没有评论

2012 年 03 月 24 日 at 下午 3:53分类:NoSQL

准备工作:
到官方下载合适的版本 http://www.mongodb.org/downloads
这里用的是mongodb windows 32bit(我本机是32的OS) 版
解压mongodb到D:\mongodb\ 目录,同时在mongodb目录中新建目录db(用于存放数据)和目录logs(存放日志),那么mongodb目录中就应该有了这些文件,如图:

在D:\mongodb\logs\ 目录下新建一个mongodb.log 用于记录安装日志,那么都准备好了,开始安装吧~

安装步骤:

1.开始–运行–cmd

2.cd D:\mongodb\bin\

3. D:\mongodb\bin>mongod –logpath D:\mongodb\logs\mongodb.log –logappend –dbpath D:\mongodb\db –directoryperdb –serviceName MongoDB –install

参数不明白的可以使用命令:D:\mongodb\bin>mongod -h

如图:

如果安装成功,则显示下面文字

all output going to: D:\mongodb\logs\MongoDB.log
Creating service MongoDB.
Service creation successful.
Service can be started from the command line via ‘net start “MongoDB”‘.

安装成功后可以到windows服务中找到mongodb服务开启或者在cmd中执行命令net start MongoDB,这样mongodb服务就启动了,mongodb的默认端口是27017

服务启动后可以执行几个简单的命令测试下:

OVER!!

MongoDB和php的mongo拓展的安装

一条评论

2011 年 12 月 19 日 at 下午 8:38分类:Cache | NoSQL | PHP

MongoDB的安装:

# wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.0.2.tgz
# tar zxvf mongodb-linux-x86_64-2.0.2.tgz
# mv mongodb-linux-x86_64-2.0.2 /usr/local/mongodb
# cd /data
# mkdir mongodb
# cd /usr/local/mongodb/
# touch logs

安装完毕!这个安装是史上最简单的了
启动MongoDB

# ./bin/mongod --dbpath=/data/mongodb/ --logpath=/usr/local/mongodb/logs.log --logappend  --port=27017 --fork --source=127.0.0.1

如果要是设置开机启动那么就把这个命令写到/etc/rc.local里面去吧!
另外这里启动有一个地方要说下,那就是关于验证的问题,这个不像Mysql任何时候都需要用户名和密码才能进入数据库,这个如果你在启动的时候,没有说明要验证的情况下,那么操作MongoDB是不需要验证的,但是如果在启动的时候设置了需要验证的话,那么在操作MongoDB就需要输入用户名和密码了。
如果需要用户的验证的那么就是在启动的命令中加入 –auth 参数,不需要的话就不用加,或者是加 –noauth
如果说开启了验证的,那么在操作MongoDB的时候,需要输入用户名密码,那么这个用户密码从哪里来呢?我一开始也纠结了一会,后来明白了,这个跟mysql数据库还是差不多的,存在一个用户表admin,这个表里面的用户就是MongoDB的管理用户列表,和mysql相似,同时,在操作具体的某个数据库的时候,还需要你去认证,而这个用户是针对当前这个数据库的,如果你先use admin,然后db.auth(‘name’,'passwd’)验证通过了,那么其他数据库就不用验证了,如果你你走这步,那么你就需要针对不同数据库,输入不同的用户名密码进行验证了,这点和mysql是一样的!
举个例子:我在MongoDB中有个数据库TEST,如果开启了验证模式,那么我想进入这个数据库进行操作,那么就需要进行db.auth(‘name’,'passwd’)进行验证,同时我可以先通过admin表的验证,那么也就通过其他所有表的验证了!增加用户的话,要先use dbname,然后在使用db.addUser(‘name’,'passwd’)就可以了。

简单的参数说明:
–logpath 日志文件路径
–master 指定为主机器
–slave 指定为从机器
–source 指定主机器的IP地址
–pologSize 指定日志文件大小不超过64M.因为resync是非常操作量大且耗时,最好通过设置一个足够大的oplogSize来避免resync(默认的 oplog大小是空闲磁盘大小的5%)。
–logappend 日志文件末尾添加
–port 启用端口号
–fork 在后台运行
–only 指定只复制哪一个数据库
–slavedelay 指从复制检测的时间间隔
–auth 是否需要验证权限登录(用户名和密码)
–noauth 不需要验证权限登录(用户名和密码)

PHP的MongoDB拓展的安装:

# wget http://pecl.php.net/get/mongo-1.2.6.tgz
# tar zxvf mongo-1.2.6.tgz
# cd mongo-1.2.6
# /usr/local/php/bin/phpize
# ./configure --enable-mongo=share --with-php-config=/usr/local/php/bin/php-config
# make && make installl 

将生成的拓展mongo.so文件添加到php.ini中,重启php-fpm,然后查看下phpinfo()

安装成功!之后就是测试了。。。

相关资料:
1:http://www.cnblogs.com/zengen/archive/2011/04/23/2025722.html
2:http://blog.csdn.net/liuyuanshijie/article/details/6735621

redis缓存的安装和使用

没有评论

2011 年 12 月 17 日 at 下午 3:03分类:Cache | Linux | NoSQL | PHP | WEB开发

首先说下什么事redis,之前在我的博客中降到很多的缓存诸如:memcache,memcached,memcachedb,dbcached等,今天讲的这个redis跟这几个差不多,redis是一个key-value存储系统。 和Memcached(这里指服务,客户端有memcache和memcached两种)类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)和zset(有序集 合)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis 支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改 操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
如果说把memcache,memcached,memcachedb,dbcached以及redis进行分类的话,我会把memcache和memcached分为一类,其实后者是前者的升级版,而memcachedb,dbcached和redis可以分为一类,因为他们在记住内存的同时会把数据写到磁盘文件中,达到持久性存储的目的,个人认为可以把这三者归结为Nosql的范畴。

在linux上面安装redis

# wget http://redis.googlecode.com/files/redis-2.4.4.tar.gz
# tar zxvf redis-2.4.4.tar.gz
# mv redis-2.4.4 /usr/local/redis
# cd /usr/local/redis
# make && make install

安装完成后,会自动copy可执行文件到环境变量中,不要自己去copy了。

# redis-
redis-benchmark   redis-check-dump  redis-server  
redis-check-aof   redis-cli

之后就是配置redis了,配置文件位于/usr/local/redis下面的redis.conf
配置项根据个人而定,一下是我的配置项:

#是否以后台进程运行,默认为no
daemonize yes
#如以后台进程运行,则需指定一个pid,默认为/var/run/redis.pid
pidfile /var/run/redis/redis.pid
#监听端口,默认为6379
port 6379
#绑定主机IP,默认值为127.0.0.1(注释)
bind 127.0.0.1
#超时时间,默认为300(秒)
timeout 300
#日志记录等级,有4个可选值,debug,verbose(默认值),notice,warning
loglevel verbose
#日志记录方式,默认值为stdout
logfile stdout
#可用数据库数,默认值为16,默认数据库为0
databases 16
#指出在多长时间内,有多少次更新操作,就将数据同步到数据文件。这个可以多个条件配合,比如默认配置文件中的设置,就设置了三个条件。
#900秒(15分钟)内至少有1个key被改变
save 900 1
#300秒(5分钟)内至少有10个key被改变
save 300 10
#存储至本地数据库时是否压缩数据,默认为yes
rdbcompression yes
#本地数据库文件名,默认值为dump.rdb
dbfilename redis.rdb
#本地数据库存放路径,默认值为 ./
dir /data/redis/
#当本机为从服务时,设置主服务的IP及端口(注释)
slaveof <masterip> <masterport>
#当本机为从服务时,设置主服务的连接密码(注释)
masterauth <master-password>
#连接密码(注释)
requirepass foobared
#最大客户端连接数,默认不限制(注释)
maxclients 128
#设置最大内存,达到最大内存设置后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理后,任到达最大内存设置,将无法再进行写入操作。(注释)
maxmemory <bytes>
#是否在每次更新操作后进行日志记录,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为redis本身同步数据文件是按上#面#save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认值为no
appendonly yes
#更新日志文件名,默认值为appendonly.aof(注释)
appendfilename /root/redis_db/appendonly.aof
#更新日志条件,共有3个可选值。no表示等操作系统进行数据缓存同步到磁盘,always表示每次更新操作后手动调用fsync()将数据写到磁盘,everysec表示每秒同步一次(默认值)。
appendfsync everysec
#是否使用虚拟内存,默认值为no
vm-enabled yes
#虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
vm-swap-file /tmp/redis.swap
#将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的 (Redis的索引数据就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认值为0。
vm-max-memory 0
#虚拟内存文件以块存储,每块32bytes
vm-page-size 32
2#虚拟内在文件的最大数
vm-pages 134217728
#可以设置访问swap文件的线程数,设置最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的.可能会造成比较长时间的延迟,但是对数据完整性有很好的保证.
vm-max-threads 4
#把小的输出缓存放在一起,以便能够在一个TCP packet中为客户端发送多个响应,具体原理和真实效果我不是很清楚。所以根据注释,你不是很确定的时候就设置成yes
glueoutputbuf yes
#在redis 2.0中引入了hash数据结构。当hash中包含超过指定元素个数并且最大的元素没有超过临界时,hash将以一种特殊的编码方式(大大减少内存使用)来存储,这里可以设置这两个临界值
hash-max-zipmap-entries 64
#hash中一个元素的最大值
hash-max-zipmap-value 512
#开启之后,redis将在每100毫秒时使用1毫秒的CPU时间来对redis的hash表进行重新hash,可以降低内存的使用。当你的使 用场景中,有非常严格的实时性需要,不能够接受Redis时不时的对请求有2毫秒的延迟的话,把这项配置为no。如果没有这么严格的实时性要求,可以设置 为yes,以便能够尽可能快的释放内存
activerehashing yes

配置好了之后来启动redis

# redis-server /usr/local/redis/redis.conf
# netstat -anpt | grep 6379   //查看进程是否启动

现在你可以用telnet来链接并进行数据的存储测试。
我们现在来安装php的redis拓展

# wget https://github.com/owlient/phpredis/tarball/master
# mv owlient-phpredis-2.1.1-1-g90ecd17.tar.gz phpredis-2.1.1-1.tar.gz
# tar zxvf phpredis-2.1.1-1.tar.gz
# mc owlient-phpredis-2.1.1-1-g90ecd17 phpredis-2.1.1
# cd  phpredis-2.1.1
# /usr/locla/php/bin/phpize
# ./configure --enable-redis=share --with-php-config=/usr/local/php/bin/php-config
# make && make install

之后在php的拓展目录下面会有redis.so文件生成,然后把这个加到php.ini中就行了,记得重启php-fpm哦。
PS:我们在安装php的拓展的时候,记得一定要加上–enable-theNameOfTheExtensions=share还有后面的php-config文件路径。虽然有些时候不会报错,但是尽量还是加上好,我曾经就因为装一个facedetect.so这个拓展,没有加那两句话,结果php一直不能认识这个拓展,然后phpinfo里面也没有这个拓展,害了我花了很长时间。
php的测试代码

<?php  
$redis = new Redis();  
$redis->connect('127.0.0.1', 6379);  
$redis->set('fbbin',serialize(array('中华人民共和国','美利坚合众国')));  
print_r(unserialize($redis->get("fbbin"))); 

能够正确输出信息,搞定。
上面的代码之所以要序列化,好像redis不支持Array类型的数据,我一开始并没有序列化,然后得出的结果就是“Array”,而不是我要的数组数据,所以我把它序列化了。到底是不支持还是我哪里的配置问题,还有待考究啊,希望哪位牛人能give me a hand。