zoukankan      html  css  js  c++  java
  • 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

    // 为什么使用 Redis及其产品定位

    http://www.infoq.com/cn/articles/tq-redis-memory-usage-optimization-storage

    // Redis内存使用优化与存储

    http://www.infoq.com/cn/articles/tq-redis-copy-build-scalable-cluster

    // Redis复制与可扩展集群搭建

    http://blog.prosight.me/index.php/2011/08/802

    // Redis中7种集合类型应用场景

    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

  • 相关阅读:
    新版SourceTree免帐号登录安装
    常用 Git 命令清单
    Linux添加/删除用户和用户组
    使用sklearn优雅地进行数据挖掘
    matplotlib 散点图scatter
    使用Python进行描述性统计
    pandas将字段中的字符类型转化为时间类型,并设置为索引
    xp系统报错 windows explorer has encountered a problem and needs to close.We are sorry for the inconvenience
    python下几种打开文件的方式
    Python 使用 Matplotlib 做图时,如何画竖直和水平的分割线或者点画线或者直线?
  • 原文地址:https://www.cnblogs.com/fvsfvs123/p/4319240.html
Copyright © 2011-2022 走看看