Redis数据存储解决方案
http://www.tuicool.com/articles/77nUZn
1、背景
1.1 Redis简介
官方网站: http://redis.io/ ,Redis是REmote DIctionary Server的缩写。
Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。它跟 memcached 类似,不过数据可以持久化,而且支持的数据类型很丰富。它在保持键值数据库简单快捷特点的同时,又吸收了部分关系数据库的优点。从而使它的位置处于关系数据库和键值数据库之间。Redis不仅能保存Strings类型的数据,还能保存Lists类型(有序)和Sets类型(无序)的数据,而且还能完成排序(SORT)等高级功能,在实现INCR,SETNX等功能的时候,保证了其操作的原子性,除此以外,还支持主从复制等功能。Redis可以被看成是一个数据结构服务器。
Redis的所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁盘上(这称为“半持久化模式”);也可以把每一次数据变化都写入到一个append only file(aof)里面(这称为“全持久化模式”)。
1.2 瓶颈
当前卡布西游项目,数据存储采用MySQL,主游戏库共分为20个库,分布在5台机器上,每个库数据量约为10G,各机器均采用RAID5加速磁盘访问;当同时在线人数达到5W时,DB磁盘IO压力很大,导致游戏卡,在线人数再上不去了。而Redis数据读写都是直接操作内存,可有效解决这一瓶颈。
1.3 解决方案概述
将部分数据压缩导入到redis后,总数据量约15G(转换成redis数据类型数据量),一主一从模型,共两组。一台机器内存32G,开20个实例,共40个实例;预估每个实例最多存储2G数据(现在没这么多)。多实例方便做持久化。
主库不做AOF,也不做快照,只在每晚12点计划任务做一次快照并转移到异地。从库开启AOF并1秒落地。
主库每5分钟插入一个时间点(crontab实现),方便从库AOF记录时间点,用于定点还原。
2、安装配置
2.1 硬件配置
Dell R410 32G内存;两个CPU,每个CPU 4个核心;300G硬盘
2.2 安装
a)安装TCMalloc库
wget http://download.savannah.gnu.org/releases/libunwind/libunwind-0.99-alpha.tar.gz
tar zxf libunwind-0.99-alpha.tar.gz
cd libunwind-0.99-alpha/
CFLAGS=-fPIC ./configure
make CFLAGS=-fPIC
make CFLAGS=-fPIC install
b)安装google-perftools:
wget http://google-perftools.googlecode.com/files/google-perftools-1.8.2.tar.gz
tar zxf google-perftools-1.7.tar.gz
cd google-perftools-1.7/
./configure
make
make install
echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local_lib.conf
/sbin/ldconfig
c)安装redis
wget http://redis.googlecode.com/files/redis-2.2.14.tar.gz
tar xzvf redis-2.2.14.tar.gz
cd redis-2.2.14
make USE_TCMALLOC=yes
//make之后会在 src目录下产生redis-server redis-cli redis-benchmark redis-check-dump
将其拷贝到/usr/local/redis/bin目录下,将配置文件redis.conf拷贝到/usr/local/redis/etc目录
//统一建立目录,方便管理
mkdir -p /usr/local/redis/bin
mkdir -p /usr/local/redis/etc
mkdir -p /usr/local/redis/var
mkdir -p /data/redis-data
# mkdir -p /data/redis-data/redis60{01,02,03,04,05,06,07,08,09,10}
检查tcmalloc是否生效
# lsof -n | grep tcmalloc
出现以下信息说明生效
redis-ser 13768 root mem REG 8,5 1616491 788696 /usr/local/lib/libtcmalloc.so.0.1.0
//装完后,可能libtcmalloc没有注册,没有上面信息,在mysqld_safe文件里加一条:
export LD_PRELOAD=/usr/local/lib/libtcmalloc.so
//再重启mysql(线上操作谨慎!!!)(使用tmalloc,内存分配效率更高)
2.3 配置
//当前规划一共开40个实例,每台机器20个实例,下面以实例01为例:
//配置文件名: redis6001.conf ~ redis6040.conf
daemonize yes
pidfile /var/run/redis6001.pid
port 6001
timeout 300
loglevel debug
logfile /usr/local/redis/var/debug6001.log
syslog-enabled yes
databases 16
rdbcompression yes
dbfilename redis6001.rdb
dir /data/redis-data/redis6001
slave-serve-stale-data yes
requirepass My#redis appendonly no
no-appendfsync-on-rewrite no
vm-enabled no
hash-max-zipmap-entries 512
hash-max-zipmap-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
activerehashing yes
3、主从同步备份及相关脚本
3.1 主从同步目的目标
主从的主要目的:数据持久化,灾难恢复,冗余。主库不落地,减少消耗。
3.2 主从配置
从库安装配置同主库一致,主库无需任何改动(允许从库访问),从库需增加两个地方配置:slaveof + AOF
slaveof 192.168.3.180 6301
masterauth My#redis
appendonly yes
appendfilename appendonly01.aof
appendfsync everysec
//配置文件名: slave6001.conf ~ slave6040.conf
daemonize yes
pidfile /var/run/slave6001.pid
port 6001
timeout 300
loglevel debug
logfile /usr/local/redis/var/debug6001.log
syslog-enabled yes
databases 16
rdbcompression yes
dbfilename slave6001.rdb
dir /data/redis-data/slave6001
slaveof 192.168.0.139 6001
masterauth My#redis slave-serve-stale-data yes
requirepass My#redis appendonly yes
appendfilename slave6001.aof
appendfsync everysec
no-appendfsync-on-rewrite no
vm-enabled no
vm-swap-file /tmp/redis.swap
vm-max-memory 0
vm-page-size 32
vm-pages 134217728
vm-max-threads 4
hash-max-zipmap-entries 512
hash-max-zipmap-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
activerehashing yes
3.3 备份
备份策略:主库每晚12点串行给每个实例做一次快照(bgsave);从库每晚12点串行对AOF文件打包备份(tar),打包备份后做一次AOF文件压缩(bgrewriteaof)。每天的数据起始点是前一天晚上rewriteaof后的数据。
主库每晚12点串行给每个实例做一次快照(bgsave)。主库备份脚本(redis_bgsave.sh)
#!/bin/bash
#date=`date +%y%m%d_%H%M%S`
REDISPASSWORD=My#redis
for PORT in `seq 6001 6020`
do
/usr/local/redis/bin/redis-cli -h 127.0.0.1 -p $PORT -a $REDISPASSWORD bgsave
sleep 10
done
date >> /usr/local/redis/var/bgsave.log
从库每晚12点串行拷贝每个实例的AOF到其他目录并对其打包,压缩包要有异地备份,之后再压缩AOF(bgrewriteaof)。
从库备份AOF并bgrewriteaof脚本(redis_backup.sh :对单个实例)
#!/bin/sh
## FD:File Dir
## RD:Runing Dir
## 第一个参数为redis实例名
if [ $# -ne 1 ]; then
echo “Usage:$0 redis_name”
exit
fi
CURDATE=`date +%Y%m%d`
CURHOUR=`date +%Y%m%d_%H`
CURTIME=`date +%Y%m%d_%H%M%S`
REDISPASSWORD=My#redis
REDISNAME=$1
PORT=`echo $REDISNAME | cut -c6-9`
LOGFILE=/data/logs/redisbak/redis_allbak_${CURDATE}.log
if [ "${REDISNAME}" = "" ]; then
echo “redis name Error!”
exit 1
else
if [ ! -d "/data/redis-data/${REDISNAME}" ]; then
echo “redis name Error!”
exit 1
fi
fi
DDIR=/data/backup/redis/$CURHOUR
mkdir -p ${DDIR}
RDIR=/data/redis-data/$REDISNAME
cd ${RDIR}
tar -zcf $DDIR/${REDISNAME}_${CURTIME}.tar.gz $REDISNAME.aof
if [ $? != 0 ]; then
echo “tar error $REDISNAME.aof” >> $LOGFILE
#exit 1
fi
sleep 5
/usr/local/redis/bin/redis-cli -h 127.0.0.1 -p $PORT -a $REDISPASSWORD bgrewriteaof
sleep 5
### delete old backup data dir ###
#/bin/rm -rf `date -d -7day +”%Y%m%d”`
find /data/backup/redis/ -mtime +7 | xargs rm -rf
echo “Backup $REDISNAME ok at $CURTIME !” >> $LOGFILE
从库对所有实例备份(/data/sh/redis_allbak.sh)
#!/bin/sh
CURDATE=`date +%Y%m%d`
LOGFILE=/data/logs/redisbak/redis_allbak_${CURDATE}.log
for PORT in `seq 6001 6020`
do
/data/sh/redis_backup.sh slave${PORT} && echo “slave${PORT} ok `date +%Y%m%d_%H%M%S`” >> $LOGFILE 2>&1 || echo “slave${PORT} backup error” >> $LOGFILE 2>&1
done
4、操作注意事项
1. 若主库挂了,不能直接开启主库程序。若直接开启主库程序将会冲掉从库的AOF文件,这样将导致只能恢复到前一天晚上12的备份。
2. 程序在跑时,不能重启网络(程序是通过网络接口的端口进行读写的)。网络中断将导致中断期间数据丢失。
3. 确定配置文件全部正确才启动(尤其不能有数据文件名相同),否则会冲掉原来的文件,可能造成无法恢复的损失。
5、灾难恢复
5.1 主库故障,快速恢复到最近状态
描述:主库挂了(redis程序挂了/机器宕机了),从库正常,恢复到主库挂掉的时间点:去从库手动做一次快照,拷贝快照到主库相应目录,启动,OK。
在从库做一次快照,转移快照文件到其他目录,将快照文件目录拷贝到主库相应目录,启动主库,OK!
( /data/sh/redis_bgsave_cp.sh )
#!/bin/bash
REDISPASSWORD=My#redis
for PORT in `seq 6001 6020`
do
/usr/local/redis/bin/redis-cli -h 127.0.0.1 -p $PORT -a $REDISPASSWORD bgsave
sleep 5
done
sleep 15
for PORT in `seq 6001 6020`
do
SDIR=/data/redis-data/slave${PORT}/
DDIR=/data/redis_recovery/redis-data/
mkdir -p $DDIR/redis${PORT}/
cd $SDIR
cp -rf slave${PORT}.rdb $DDIR/redis${PORT}/redis${PORT}.rdb
#sleep 1
done
在主库将原来数据目录重命名。
从从库拷贝快照文件到主库。
启动主库。
5.2 恢复到当天12点状态
注意备份数据(当前状态AOF+正常退出快照)!
停止redis。
解压AOF(/data/sh/redis_untar.sh)
#!/bin/bash
DAY=20111102
SDIR=/data/backup/redis/20111102_00/
cd $SDIR
for PORT in `seq 6001 6020`
do
tar -zxf slave${PORT}_$DAY_*.tar.gz
sleep 2
done
切割AOF(/data/sh/redis_sed.sh)
#!/bin/bash
DDIR=/data/redis_recovery/
TAG=”TAG111101_1200″
for PORT in `seq 6001 6020`
do
SDIR=/data/backup/redis/20111102_00/
SAOF=${SDIR}/slave${PORT}.aof
line=`sed -n “/$TAG/=” $SAOF`
num=$[$line + 3]
mkdir -p ${DDIR}/slave${PORT}/
sed “${num},\$d” $SAOF > ${DDIR}/slave${PORT}/slave${PORT}.aof
done
将原来数据目录重命名。
将切割出来的AOF目录重命名为配置文件的数据目录。(注释主从同步配置项)。
启动从库。
做快照,拷贝到主库,启动主库(同5.1)。
5.3 恢复到两天或几天前12点状态
从库每晚备份要备份AOF未bgrewriteaof之前的数据,可根据当天晚上12点备份,没有bfrewriteaof之前的AOF文件来进行恢复,方法同5.2。
6、监控
6.1 Nagios监控
# /usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 6301 -a My#redis info
used_memory_human:1.49G
master_link_status:down up/down
db0:keys=2079581,expires=0
//监控进程数(每台机器redis实例数)
define service{
use local-service
host_name 119.147.163.xxx
servicegroups procs
service_description redis_20
check_command check_nrpe!check_procs\!20:20\!20:25\!redis-server
}
//监控主从同步状态(获取从库master_link_status与keys的值)
//status=down直接严重警告,keys相差10警告,keys相差100严重警告
define service{
use local-service
# hostgroup_name kabuxiyou
host_name 119.147.161.xxx
servicegroups redis
service_description redis_239
check_command check_nrpe!check_redis_slave\!192.168.0.139\!6379\!127.0.0.1\!6380
}
//监控实例内存使用大小,1.8G警告,2G严重警告
define service{
use local-service
# hostgroup_name kabuxiyou
host_name 119.147.161.xxx
servicegroups redis
service_description redis_mem_22
check_command check_nrpe!check_redis_mem\!127.0.0.1\!6381
}
7、资料
7.1 参考资料
Redis入门手册(zh_v1.0).pdf
http://www.infoq.com/cn/articles/tq-why-choose-redis
http://www.infoq.com/cn/articles/tq-redis-memory-usage-optimization-storage
http://www.infoq.com/cn/articles/tq-redis-copy-build-scalable-cluster
http://blog.prosight.me/index.php/2011/08/802
7.2 版本
最新稳定版:
wget http://redis.googlecode.com/files/redis-2.2.14.tar.gz // 2011.10.1
wget http://redis.googlecode.com/files/redis-2.4.2.tar.gz // 2011.11.15
wget http://redis.googlecode.com/files/redis-2.4.10.tar.gz // 2012.04.16
8、卡布西游Redis分布
8.1 服务器分布
服务器分布: 实例名: 端口:
… … …
8.2 mysql转redis规则
1.Key命名规则
Key采用字母+数字的形式 避免多个应用使用同一个key的时候互相覆盖,主项目中的key采用GJ#[UID] 例:GJ12345678
2.字段命名规则
主项目中的key内包含多个字段 代表多个应用 目前给每个应用预留的字段取值范围为100(实际使用中字段不应该超过10个左右不然使用哈希的意义不大了)。
当前的预留字段划分:
100~199为成就相关(当前使用150、151 对应MySQL的achieve_user表的history与current字段)
200~299为妖怪相关(当前使用200 对应MySQL的spirit_store表 实际应用时应该会再多2个字段 对应MySQL的spiritmaster与spirit表)
3.实例划分规则
预计将划分40个实例 玩家数据根据玩家的UID取模来分配到不同的实例中(是否再分库未确定)
4.其他使用Redis划分规则
目前庄园也有部分数据使用redis 不过数据是放在一个单独的redis实例中(119.147.164.24)
根据应用不同使用不同的库和key值 Key命名规则为[应用名]-[UID] 例: GrowTimes-12345678 / StolenList-12345678
9、相关测试数据
//测试数据跟存储的数据以及机器硬件配置等有关,具体环境可能不太一样,仅做参考。
9.1 启动关闭
启动:1.6G AOF文件,132M RDB,启动时间107s,产生debug.log 128M,使用内存1.71G。
2G数据,启动约2分钟,主从同步约2分钟(内网)。
关闭:shutdown时间很短,一般1~2s,shutdown之前必须做save,save时间跟数据量有关,一般几秒。
9.2 数据量大小
简单set(set a 1),3万条约1M AOF,300K RDB;12万条约4M AOF,1M RDB。
set一条记录,AOF会记录些什么:
redis> set yuwei wangjiancheng
*3
$3
set
$5
yuwei
$3
wangjiancheng
9.3 备份打包
纯文本AOF:
tar -zcf *.tar.gz * 1.9G / 20s / 16M /
tar -jcf *.tar.bz2 * 1.9G / 150s / 5.3M /
当前线上使用redis,数据导入是二进制流格式(AOF文件为数据文件,非文本文件),而且导入前已经过一次压缩,所以压缩比例很低。
9.4 其他测试
实例配置文件中的数据目录不存在,实例无法启动。
AOF边写边打包(tar),不会造成数据损坏,tar只是读文件。
Redis运行过程中删除数据目录,不能bgsave,不能写AOF,从库能同步;从建目录也不能bgsave,不能写AOF。
Bgsave是放后台去执行的,所以bgsave;shutdown连写在一起会出问题,bgsave还未执行完就已经shutdown了。改用save。
主库实例重启后,从库会重新”全同步”一次,保存数据与主库一致。
网络闪断不会造成主从数据丢失,网络恢复后可以续传。
A实例快照文件,放B实例数据目录启动(文件名改成B实例数据文件),OK!
执行rewriteaof/ bgrewriteaof后,reidis根据内存数据,重新建立AOF,AOF重新排列顺序。
10、客户端连接redis
10.1 PHP
安装phpredis:
phpredis软件包地址(试了N个版本 只有这1个能在我们的定制系统环境装上 蛋疼)
http://ftp.nluug.nl/pub/NetBSD/packages/distfiles/php-redis/nicolasff-phpredis-2.1.3-0-g43bc590.tar.gz
cd /dist/src
tar xzf ../dist/nicolasff-phpredis-2.1.3-0-g43bc590.tar.gz
cd nicolasff-phpredis-43bc590
/usr/local/php/bin/phpize
## 如果PHP是安装的RPM包,需要安装php-devel
./configure –with-php-config=/usr/local/php/bin/php-config
make && make install
配置php.ini
vi /usr/local/php/lib/php.ini
加入:
extension = “redis.so”
重启 fastcgi 生效 /root/fastcgi_restart
# php -m //查看是否有redis,检查是否安装成功。
Redis扩展测试
测试代码:
<?php$redis = new Redis();$redis->connect(’127.0.0.1′,6379);$redis->set(‘test’,'hello world!’);echo $redis->get(‘test’);
?> |
返回 hello world !表示成功
## 密码
$redis->auth(‘My#redis’);
## Error:
configure: error: invalid value of canonical build
## ./configure
./configure –with-php-config=/usr/local/php/bin/php-config
10.2 C++
antirez-hiredis-3cc6a7f.tar.gz 340 KB
这个是官方的库,里面有个例子
跑这个,带参数 ./hiredis-test
例子应该是test.c
Redis数据存储解决方案的更多相关文章
- 浏览器本地数据存储解决方案以及cookie的坑
本地数据存储解决方案以及cookie的坑 问题: cookie过长导致页面打开失败 背景: 在公司的项目中有一个需求是打开多个工单即在同一个页面中打开了多个tab(iframe),但是需要在刷新时只刷 ...
- 本地数据存储解决方案以及cookie的坑
本地数据存储解决方案以及cookie的坑 问题: cookie过长导致页面打开失败 背景: 在公司的项目中有一个需求是打开多个工单即在同一个页面中打开了多个tab(iframe),但是需要在刷新时只刷 ...
- redis数据存储的细节
redis是一个K-V NoSql非关系型数据库,redis有物种数据类型,分别是String,Hash,list,set,zset:这五种类型都是针对K-V中的V设计的. 1.总体介绍:关于redi ...
- 转载:MySQL和Redis 数据同步解决方案整理
from: http://blog.csdn.net/langzi7758521/article/details/52611910 最近在做一个Redis箱格信息数据同步到数据库Mysql的功能. 自 ...
- Redis数据存储优化机制(转)
原文:Redis学习笔记4--Redis数据存储优化机制 1.zipmap优化hash: 前面谈到将一个对象存储在hash类型中会占用更少的内存,并且可以更方便的存取整个对象.省内存的原因是新建一个h ...
- 直接在安装了redis的Linux机器上操作redis数据存储类型--String类型
一.概述: 字符串类型是Redis中最为基础的数据存储类型,它在Redis中是二进制安全的,这便意味着该类型可以接受任何格式的数据,如JPEG图像数据或Json对象描述信息等.在Redis中字符串类型 ...
- Redis数据存储结构之String
前言: 在Redis使用中,我们最常使用的操作是set key value,或 get key value .这里面包含了redis最基本的数据类型:String,字符串类型是redis中最基本的类型 ...
- 第十三节:web爬虫之Redis数据存储
下面仅仅展示Redis的set()集合存储,并不完整,后期会对Redis进行全面的介绍.... 此时数据已经存储到Redis当中
- redis数据存储--C++连接redis
一.下载的是Redis Windows版本:下载地址:https://github.com/microsoftarchive/redis:解压到:E:\Software\redis-3.0: 二.用V ...
随机推荐
- ListView-divider 分割线的设置
1.去掉分割线 android:divider="@null" 2.设置分割线颜色跟宽度 android:divider="#19000000" android ...
- rhel5安装 oracle10
readhat 安装11gr2文档 需要注意的地方:必须关掉的 1,防火墙:2,SElinux . root 用户运行 setup 命令可关防火墙与SElinux 修改网络配置文件,一定要重启此文 ...
- 一个简单http请求的jmeter压测实战流程
1.新建线程组 2.创建http请求 注意:接口路径中的参数值要写变量 3.创建txt文件,存多个参数值 4.创建csv文件,在csv中上传txt文件 5.variable name填写txt中参数值 ...
- Numpy库进阶教程(一)求解线性方程组
前言 Numpy是一个很强大的python科学计算库.为了机器学习的须要.想深入研究一下Numpy库的使用方法.用这个系列的博客.记录下我的学习过程. 系列: Numpy库进阶教程(二) 正在持续更新 ...
- hunnu11550:欧拉函数
Problem description 一个数x的欧拉函数Φ(x)定义为全部小于x的正整数中与x互质的数的数目,如小于5且和5互质的数有1.2.3.4,一共4个,故Φ(5)=4. 对于随意正整数x ...
- Maven使用yuicompressor-maven-plugin打包压缩css、js文件
最近项目想使用在maven打包的时间压缩js,css文件,采用yuicompressor-maven-plugin插件进行压缩,但只是压缩减小大小,提高请求速度,并没有对js进行混淆.下面就写一下这个 ...
- WPF学习笔记——概述
如果你选择WPF,多半原因是因为折服于它那震撼性的用户体验.纵观WPF整个知识体系,其内容并不复杂,但却比较细碎,不易理清.以下内容是对WPF部分内容的简单概括,希望读者能够对WPF框架有个大体认识. ...
- mybatis-generator使用
开发工具:eclipse,依赖环境 :maven 首先在eclipse marketplace中安装mybatis-generator,如图: 安装后需要重启. 新建一个maven项目MybatisG ...
- 15、NAND FLASH驱动程序框架
驱动可以参考At91_nand.c,这个比S3c2410.c (drivers\mtd\nand)简单多了 NAND FLASH是一个存储芯片那么: 这样的操作很合理"读地址A的数据,把数据 ...
- 将OpenCV捕获的摄像头加载到picture控件中
CRect rect; CStatic* pStc; CDC* pDC; HDC hDC; pStc = (CStatic*)GetDlgItem(IDC_CAM);//IDC_CAM是Picture ...