转:http://litaocheng.iteye.com/blog/787623
最近项目中使用Redis比较多,非常喜欢这个小巧而强大的家伙。
准备写一个简单的系列,讲述使用Redis可以做什么,我们将充分挖掘Redis的潜能。
这是第一篇,准备用Redis作为Log汇总。
Redis介绍
Reids是一个比较高级的开源key-value存储系统,采用ANSI C实现。其与memcached类似,但是支持持久化数据存储,同时value支持多种类型:字符串 (同memcached中的value),列表 ,集合 (Set),有序集合 (OrderSet)和Hash 。所有的值类型均支持原子操作,如列表中追加弹出元素,集合中插入移除元素等。Rdids的数据大部分位于内存中,其读写效率非常高,其提供AOF(追加式操作记录文件)和DUMP(定期数据备份)两种持久化方式。Redis支持自定义的VM(虚拟内存)机制,当数据容量超过内存时,可以将部分Value 存储到文件中。同时Redis支持Master-Slave机制,可以进行数据复制。
Redis目前有VMWare资助,其作者antirez也就职于VMWare。
我们可以这样理解Redis,其对传统的key-value系统中的Value进行了扩展,Value不再是一个二进制字符,其可以包含多种类型。多种数据类型的引入,可以大大的简化我们应用的开发。比如我们应用中,需要一个名为“L1”的列表,用来顺序记录用户ID,一般的key-value系统中,我们会设置key为"L1",value为列表序列化处理后的结果。每次要对L1操作时,需要下列步骤:
1. 取出key L1的值
2. 进行反序列化
3. 对列表进行增删操作
4. 进行序列化
5. 存储结果到key L1对应的value
一次更新需要经历5个步骤,包含序列化/反序列化,以及两次网络交互。
如果我们使用Redis的列表类型来存储Value,那么其步骤如下:
1. 列表增删操作
对,只有一步操作!由此可见,Redis在某些情景下会大大的简化我们的应用逻辑。
日志汇总
我们的系统中包含很多节点(分布在不同的机器),我们需要一个地方来汇总所有的数据,进行入库(mysql,hadoop)操作。要尽可能的减少系统的复杂度,这方面的项目已经很多,解决方法也有很多。以前尝试过Scribe,其为Facebook的开源项目,基于Thrift,显然有些庞大了,同样繁琐还有使用MQ(消息中间件,如RabbitMQ)。对于小团队,这些方案都不适合。而其他各种山寨的办法,如日志写入文件,定期拷贝等等都太松散,不易管理。因为在项目中使用了Redis,所以决定把它用透彻,尝试进行Log的中转。
如果使用Redis进行Log汇总,可能首先要考虑下面几个问题:
1. Redis是否会成为瓶颈
2. Redis是否会成为一个单点故障
3. Redis进行日志数据的汇总和提取是否方便
第1个问题,Redis的性能很出色,数据首先是放在内存中。此外系统中主要记录一些关键的操作日志,日志的量不会很大大,因此不会成为性能瓶颈。具体的Redis性能评测参考这里 。
第2个问题,Redis是否成为单点故障,如果只有一台Redis服务器,其宕掉,日志肯定无法发送成功。可以有多种选择,在Client端存储发送失败的Log,Redis恢复时重发(这样做增加了系统复杂性,Client会涉及重试,Log临时缓存大小控制等);或者可以在加一台Redis服务器作为备份。当然如果你的Log不是非常重要,允许一些丢失,那一台也无妨,只要你能保证Redis服务器可以及时的发现问题,并很快恢复就好。
第3个我呢题,Redis进行数据汇总很方便,我们可以直接将每条日志都追加到一个列表中。我们设置可以为日志分类,比如日志分error和 debug两种,那么我们可以在Redis中创建两个列表:key为"error",value为List;和key为"debug",value为 List。增加一条日志,仅仅是一个列表追加操作(Redis命令RPUSH)。而提取日志也非常方便,我们可以使用一个阻塞的列表弹出操作(Redis 命令BLPOP,从队列头部弹出元素),当队列为空,即没有日志时,将阻塞。直到有日志产生。这个操作循环执行,就可以提取所有的日志,然后进行分析处理直接入库。
原型
现在做一件事情之前,我跟喜欢做一个原型,作为验证。下面我用Bash来模拟一下这个日志的发送和处理。
1, 启动Redis服务器
Erlang代码 收藏代码
1. $ redis-server redis.conf
$ redis-server redis.conf
2, 书写gen_log.sh,代码如下:
Shell代码 收藏代码
1. #! /bin/bash
2.
3. COUNT=0
4.
5. while true; do
6. LOG=`date '+%F %T'`
7. LOG="${LOG} this is a log record"
8.
9. if !( redis-cli LPUSH log "${LOG}" > /dev/null 2>&1 ); then
10. echo "send log error!"
11. exit 1
12. fi
13. COUNT=$((++COUNT))
14.
15. echo "sent ${COUNT} logs"
16. sleep 2
17. done
#! /bin/bash
COUNT=0
while true; do
LOG=`date '+%F %T'`
LOG="${LOG} this is a log record"
if !( redis-cli LPUSH log "${LOG}" > /dev/null 2>&1 ); then
echo "send log error!"
exit 1
fi
COUNT=$((++COUNT))
echo "sent ${COUNT} logs"
sleep 2
done
随后运行:
Shell代码 收藏代码
1. $ chmod +x gen_log.sh & ../gen_log.sh
2. sent 1 logs
3. sent 2 logs
$ chmod +x gen_log.sh & ../gen_log.sh
sent 1 logs
sent 2 logs
从输出得知,已经开始发送日志。
我们可以启动多个gen_log.sh,模拟多个client。
此时可以查看log队列是否增加:
Shell代码 收藏代码
1. $ redis-cli
2. redis > LLEN log
3. 32
$ redis-cli
redis > LLEN log
32
可以看到log已经产生。
3, 书写analy_log.sh,其代码如下:
Shell代码 收藏代码
1. #! /bin/bash
2.
3. COUNT=0
4.
5. while true; do
6. LOG=$(redis-cli BLPOP log 0 2> /dev/null)
7. if [ $? -ne 0 ]; then
8. echo "get log error!"
9. exit 1
10. fi
11. COUNT=$((++COUNT))
12.
13. echo "(${COUNT}) analyzing ${LOG} ..."
14. #sleep 1
15. done
#! /bin/bash
COUNT=0
while true; do
LOG=$(redis-cli BLPOP log 0 2> /dev/null)
if [ $? -ne 0 ]; then
echo "get log error!"
exit 1
fi
COUNT=$((++COUNT))
echo "(${COUNT}) analyzing ${LOG} ..."
#sleep 1
done
执行
Shell代码 收藏代码
1. $ ./analy_log.sh
2. (1) analyzing log
3. 2010-10-18 17:20:58 this is a log record ...
4. (2) analyzing log
5. 2010-10-18 17:20:58 this is a log record ...
6. (3) analyzing log
7. 2010-10-18 17:20:56 this is a log record ...
8. (4) analyzing log
9. 2010-10-18 17:20:56 this is a log record ...
$ ./analy_log.sh
(1) analyzing log
2010-10-18 17:20:58 this is a log record ...
(2) analyzing log
2010-10-18 17:20:58 this is a log record ...
(3) analyzing log
2010-10-18 17:20:56 this is a log record ...
(4) analyzing log
2010-10-18 17:20:56 this is a log record ...
可以看到Log被快速的处理。
gen_log中,我们加入了一个sleep,所以analy_log可以很快的取出所有的log,通过redis-cli查看大部分log队列为空。因为处理log的速度远远大于生成log的速度。如果生成log的速度远远大于处理log的速度也没有关系,redis会相当于一个缓冲。
好了,最后你可以把上面的代码,翻译成你使用的语言,如Erlang,PHP,Python,方便的处理日志了。
最近项目中使用Redis比较多,非常喜欢这个小巧而强大的家伙。
准备写一个简单的系列,讲述使用Redis可以做什么,我们将充分挖掘Redis的潜能。
这是第一篇,准备用Redis作为Log汇总。
Redis介绍
Reids是一个比较高级的开源key-value存储系统,采用ANSI C实现。其与memcached类似,但是支持持久化数据存储,同时value支持多种类型:字符串 (同memcached中的value),列表 ,集合 (Set),有序集合 (OrderSet)和Hash 。所有的值类型均支持原子操作,如列表中追加弹出元素,集合中插入移除元素等。Rdids的数据大部分位于内存中,其读写效率非常高,其提供AOF(追加式操作记录文件)和DUMP(定期数据备份)两种持久化方式。Redis支持自定义的VM(虚拟内存)机制,当数据容量超过内存时,可以将部分Value 存储到文件中。同时Redis支持Master-Slave机制,可以进行数据复制。
Redis目前有VMWare资助,其作者antirez也就职于VMWare。
我们可以这样理解Redis,其对传统的key-value系统中的Value进行了扩展,Value不再是一个二进制字符,其可以包含多种类型。多种数据类型的引入,可以大大的简化我们应用的开发。比如我们应用中,需要一个名为“L1”的列表,用来顺序记录用户ID,一般的key-value系统中,我们会设置key为"L1",value为列表序列化处理后的结果。每次要对L1操作时,需要下列步骤:
1. 取出key L1的值
2. 进行反序列化
3. 对列表进行增删操作
4. 进行序列化
5. 存储结果到key L1对应的value
一次更新需要经历5个步骤,包含序列化/反序列化,以及两次网络交互。
如果我们使用Redis的列表类型来存储Value,那么其步骤如下:
1. 列表增删操作
对,只有一步操作!由此可见,Redis在某些情景下会大大的简化我们的应用逻辑。
日志汇总
我们的系统中包含很多节点(分布在不同的机器),我们需要一个地方来汇总所有的数据,进行入库(mysql,hadoop)操作。要尽可能的减少系统的复杂度,这方面的项目已经很多,解决方法也有很多。以前尝试过Scribe,其为Facebook的开源项目,基于Thrift,显然有些庞大了,同样繁琐还有使用MQ(消息中间件,如RabbitMQ)。对于小团队,这些方案都不适合。而其他各种山寨的办法,如日志写入文件,定期拷贝等等都太松散,不易管理。因为在项目中使用了Redis,所以决定把它用透彻,尝试进行Log的中转。
如果使用Redis进行Log汇总,可能首先要考虑下面几个问题:
1. Redis是否会成为瓶颈
2. Redis是否会成为一个单点故障
3. Redis进行日志数据的汇总和提取是否方便
第1个问题,Redis的性能很出色,数据首先是放在内存中。此外系统中主要记录一些关键的操作日志,日志的量不会很大大,因此不会成为性能瓶颈。具体的Redis性能评测参考这里 。
第2个问题,Redis是否成为单点故障,如果只有一台Redis服务器,其宕掉,日志肯定无法发送成功。可以有多种选择,在Client端存储发送失败的Log,Redis恢复时重发(这样做增加了系统复杂性,Client会涉及重试,Log临时缓存大小控制等);或者可以在加一台Redis服务器作为备份。当然如果你的Log不是非常重要,允许一些丢失,那一台也无妨,只要你能保证Redis服务器可以及时的发现问题,并很快恢复就好。
第3个我呢题,Redis进行数据汇总很方便,我们可以直接将每条日志都追加到一个列表中。我们设置可以为日志分类,比如日志分error和 debug两种,那么我们可以在Redis中创建两个列表:key为"error",value为List;和key为"debug",value为 List。增加一条日志,仅仅是一个列表追加操作(Redis命令RPUSH)。而提取日志也非常方便,我们可以使用一个阻塞的列表弹出操作(Redis 命令BLPOP,从队列头部弹出元素),当队列为空,即没有日志时,将阻塞。直到有日志产生。这个操作循环执行,就可以提取所有的日志,然后进行分析处理直接入库。
原型
现在做一件事情之前,我跟喜欢做一个原型,作为验证。下面我用Bash来模拟一下这个日志的发送和处理。
1, 启动Redis服务器
Erlang代码 收藏代码
1. $ redis-server redis.conf
$ redis-server redis.conf
2, 书写gen_log.sh,代码如下:
Shell代码 收藏代码
1. #! /bin/bash
2.
3. COUNT=0
4.
5. while true; do
6. LOG=`date '+%F %T'`
7. LOG="${LOG} this is a log record"
8.
9. if !( redis-cli LPUSH log "${LOG}" > /dev/null 2>&1 ); then
10. echo "send log error!"
11. exit 1
12. fi
13. COUNT=$((++COUNT))
14.
15. echo "sent ${COUNT} logs"
16. sleep 2
17. done
#! /bin/bash
COUNT=0
while true; do
LOG=`date '+%F %T'`
LOG="${LOG} this is a log record"
if !( redis-cli LPUSH log "${LOG}" > /dev/null 2>&1 ); then
echo "send log error!"
exit 1
fi
COUNT=$((++COUNT))
echo "sent ${COUNT} logs"
sleep 2
done
随后运行:
Shell代码 收藏代码
1. $ chmod +x gen_log.sh & ../gen_log.sh
2. sent 1 logs
3. sent 2 logs
$ chmod +x gen_log.sh & ../gen_log.sh
sent 1 logs
sent 2 logs
从输出得知,已经开始发送日志。
我们可以启动多个gen_log.sh,模拟多个client。
此时可以查看log队列是否增加:
Shell代码 收藏代码
1. $ redis-cli
2. redis > LLEN log
3. 32
$ redis-cli
redis > LLEN log
32
可以看到log已经产生。
3, 书写analy_log.sh,其代码如下:
Shell代码 收藏代码
1. #! /bin/bash
2.
3. COUNT=0
4.
5. while true; do
6. LOG=$(redis-cli BLPOP log 0 2> /dev/null)
7. if [ $? -ne 0 ]; then
8. echo "get log error!"
9. exit 1
10. fi
11. COUNT=$((++COUNT))
12.
13. echo "(${COUNT}) analyzing ${LOG} ..."
14. #sleep 1
15. done
#! /bin/bash
COUNT=0
while true; do
LOG=$(redis-cli BLPOP log 0 2> /dev/null)
if [ $? -ne 0 ]; then
echo "get log error!"
exit 1
fi
COUNT=$((++COUNT))
echo "(${COUNT}) analyzing ${LOG} ..."
#sleep 1
done
执行
Shell代码 收藏代码
1. $ ./analy_log.sh
2. (1) analyzing log
3. 2010-10-18 17:20:58 this is a log record ...
4. (2) analyzing log
5. 2010-10-18 17:20:58 this is a log record ...
6. (3) analyzing log
7. 2010-10-18 17:20:56 this is a log record ...
8. (4) analyzing log
9. 2010-10-18 17:20:56 this is a log record ...
$ ./analy_log.sh
(1) analyzing log
2010-10-18 17:20:58 this is a log record ...
(2) analyzing log
2010-10-18 17:20:58 this is a log record ...
(3) analyzing log
2010-10-18 17:20:56 this is a log record ...
(4) analyzing log
2010-10-18 17:20:56 this is a log record ...
可以看到Log被快速的处理。
gen_log中,我们加入了一个sleep,所以analy_log可以很快的取出所有的log,通过redis-cli查看大部分log队列为空。因为处理log的速度远远大于生成log的速度。如果生成log的速度远远大于处理log的速度也没有关系,redis会相当于一个缓冲。
好了,最后你可以把上面的代码,翻译成你使用的语言,如Erlang,PHP,Python,方便的处理日志了。