redis应用场景(2)日志记录及指标统计

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介:

使用redis存储业务信息,同时也可以存储系统运维信息,比如日志和计数器来收集系统当前的状态信息,挖掘正在使用系统的顾客信息,以及诊断系统问题,发现潜在的问题。当然,系统日志信息及统计信息也可以存储在关系型数据库中,但是存在一个很大的弊端,影响业务性能。


1.使用redis记录日志

熟悉java的朋友,记录日志往往采用的是log4j,sl4j,大多记录载体选择文本文件。如果使用web集群的话,造成日志分散在各个web服务器,搜集有效日志信息,非常麻烦。如果选择数据库保存的话,解决了文件分散情况,但势必对业务造成影响,日志毕竟是个辅助支撑而已,不应该和业务系统相提并论。这时候,redis是一个不错的选择。如果可以的话,可以对log4j扩展,将数据保存到redis中,当然这不是本章的重点。本章重点,主要简单讨论下如何保存日志。

构建一个系统,判断哪些信息需要被记录是一件困难的事情,不同的业务有不同的需求。但一般的日志信息往往关注一下方面。

日志时间,日志内容,服务IP,日志级别,日志发生频率。

1.1redis日志存储设计

wKiom1fbcneAyJyaAAA3q53JGAQ005.png

记录详情里,可以按要求,增添想要的信息,发生的类名称,处理IP等。

1.2代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public  void  logCommon(
         Jedis conn, String name, String message, String severity,  int  timeout) {
     String commonDest =  "common:"  + name +  ':'  + severity;
     String startKey = commonDest +  ":start" ;
     long  end = System.currentTimeMillis() + timeout;
     while  (System.currentTimeMillis() < end){
         conn.watch(startKey);
         //当前所处的小时数
         String hourStart = ISO_FORMAT.format( new  Date());
         String existing = conn.get(startKey);
 
         Transaction trans = conn.multi();
         //如果记录的是上一个小时的日志
         if  (existing !=  null  && COLLATOR.compare(existing, hourStart) <  0 ){
             trans.rename(commonDest, commonDest +  ":last" );
             trans.rename(startKey, commonDest +  ":pstart" );
             trans.set(startKey, hourStart);
         } else {
             trans.set(startKey, hourStart);
         }
          //日志计数器增1
         trans.zincrby(commonDest,  1 , message);
         //记录最近日志详情
         String recentDest =  "recent:"  + name +  ':'  + severity;
         trans.lpush(recentDest, TIMESTAMP.format( new  Date()) +  ' '  + message);
         trans.ltrim(recentDest,  0 99 );
         List<Object> results = trans.exec();
         // null response indicates that the transaction was aborted due to
         // the watched key changing.
         if  (results ==  null ){
             continue ;
         }
         return ;
     }
}

2.网站点击量计数器统计

2.1redis计数器存储设计

wKioL1fbfrzDb0BzAABPubjNr2c322.png

2.2编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//以秒为单位的精度
public  static  final  int [] PRECISION =  new  int []{ 1 5 60 300 3600 18000 86400 };
public  void  updateCounter(Jedis conn, String name,  int  count,  long  now){
     Transaction trans = conn.multi();
     //每一次更新,都要更新所有精度的计数器
     for  ( int  prec : PRECISION) {
         long  pnow = (now / prec) * prec; //当前时间片的开始时间
         String hash = String.valueOf(prec) +  ':'  + name;
         trans.zadd( "known:" 0 , hash);
         trans.hincrBy( "count:"  + hash, String.valueOf(pnow), count);
     }
     trans.exec();
}
 
public  List<Pair<Integer,Integer>> getCounter(
     Jedis conn, String name,  int  precision)
{
     String hash = String.valueOf(precision) +  ':'  + name;
     Map<String,String> data = conn.hgetAll( "count:"  + hash);
     ArrayList<Pair<Integer,Integer>> results =
         new  ArrayList<Pair<Integer,Integer>>();
     for  (Map.Entry<String,String> entry : data.entrySet()) {
         results.add( new  Pair<Integer,Integer>(
                     Integer.parseInt(entry.getKey()),
                     Integer.parseInt(entry.getValue())));
     }
     Collections.sort(results);
     return  results;
}


2.3清楚旧数据


流程图

wKiom1fbhyPh_L_UAABqzIhA1As000.png

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public class CleanCountersThread
     extends Thread
{
     private Jedis conn;
     private int sampleCount = 100;
     private boolean quit;
     private long timeOffset;  //  used to mimic a  time  in  the future.
 
     public CleanCountersThread(int sampleCount, long timeOffset){
         this.conn = new Jedis( "192.168.163.156" );
         this.conn. select (15);
         this.sampleCount = sampleCount;
         this.timeOffset = timeOffset;
     }
 
     public void quit(){
         quit =  true ;
     }
 
     public void run(){
         int passes = 0;
         while  (!quit){
             long start = System.currentTimeMillis() + timeOffset;
             int index = 0;
             while  (index < conn.zcard( "known:" )){
                 Set<String> hashSet = conn.zrange( "known:" , index, index);
                 index++;
                 if  (hashSet.size() == 0) {
                     break ;
                 }
                 String  hash  = hashSet.iterator().next();
                 int prec = Integer.parseInt( hash .substring(0,  hash .indexOf( ':' )));
                 int bprec = (int)Math.floor(prec / 60);
                 if  (bprec == 0){
                     bprec = 1;
                 }
                 if  ((passes % bprec) != 0){
                     continue ;
                 }
 
                 String hkey =  "count:"  hash ;
                 String cutoff = String.valueOf(
                     ((System.currentTimeMillis() + timeOffset) / 1000) - sampleCount * prec);
                 ArrayList<String> samples = new ArrayList<String>(conn.hkeys(hkey));
                 Collections. sort (samples);
                 int remove = bisectRight(samples, cutoff);
 
                 if  (remove != 0){
                     conn.hdel(hkey, samples.subList(0, remove).toArray(new String[0]));
                     if  (remove == samples.size()){
                         conn. watch (hkey);
                         if  (conn.hlen(hkey) == 0) {
                             Transaction trans = conn.multi();
                             trans.zrem( "known:" hash );
                             trans. exec ();
                             index--;
                         } else {
                             conn.unwatch();
                         }
                     }
                 }
             }
 
             passes++;
             long duration = Math.min(
                 (System.currentTimeMillis() + timeOffset) - start + 1000, 60000);
             try {
                 sleep (Math.max(60000 - duration, 1000));
             }catch(InterruptedException ie){
                 Thread.currentThread().interrupt();
             }
         }
     }
 
     //  mimic python's bisect.bisect_right
     public int bisectRight(List<String> values, String key) {
         int index = Collections.binarySearch(values, key);
         return  index < 0 ? Math.abs(index) - 1 : index + 1;
     }
}

3.使用redis统计数据

上面提到的计数器,是最简单的统计数据。除了计数器(count(*)),还是最大值(max),最小值(min).

设计

wKiom1fbiXig10-bAABFDHnJJ6Y518.png

stats:模块(页面)名称:指标名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public  List<Object> updateStats(Jedis conn, String context, String type,  double  value){
     int  timeout =  5000 ;
     String destination =  "stats:"  + context +  ':'  + type;
     String startKey = destination +  ":start" ;
     long  end = System.currentTimeMillis() + timeout;
     while  (System.currentTimeMillis() < end){
         conn.watch(startKey);
         String hourStart = ISO_FORMAT.format( new  Date());
 
         String existing = conn.get(startKey);
         Transaction trans = conn.multi();
         if  (existing !=  null  && COLLATOR.compare(existing, hourStart) <  0 ){
             trans.rename(destination, destination +  ":last" );
             trans.rename(startKey, destination +  ":pstart" );
             trans.set(startKey, hourStart);
         }
         //借助redis提供的最大值,最小值计算        
         String tkey1 = UUID.randomUUID().toString();
         String tkey2 = UUID.randomUUID().toString();
         trans.zadd(tkey1, value,  "min" );
         trans.zadd(tkey2, value,  "max" );
 
         trans.zunionstore(
             destination,
             new  ZParams().aggregate(ZParams.Aggregate.MIN),
             destination, tkey1);
         trans.zunionstore(
             destination,
             new  ZParams().aggregate(ZParams.Aggregate.MAX),
             destination, tkey2);
 
         trans.del(tkey1, tkey2);
         trans.zincrby(destination,  1 "count" );
         trans.zincrby(destination, value,  "sum" );
         trans.zincrby(destination, value * value,  "sumsq" );
 
         List<Object> results = trans.exec();
         if  (results ==  null ){
             continue ;
         }
         return  results.subList(results.size() -  3 , results.size());
     }
     return  null ;
}

需要注意的使用redis自带的最大值最小值,计算,所以创建了2个临时有序集合。其他的逻辑参照日志相关部分。




本文转自 randy_shandong 51CTO博客,原文链接:http://blog.51cto.com/dba10g/1853096,如需转载请自行联系原作者

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
15天前
|
存储 监控 NoSQL
Redis HyperLogLog: 高效统计大数据集的神秘利器
Redis HyperLogLog: 高效统计大数据集的神秘利器
|
22天前
|
缓存 监控 NoSQL
Redis 的监控指标
Redis 的监控指标
22 0
|
2月前
|
存储 算法 NoSQL
探秘HyperLogLog:Redis中的基数统计黑科技
探秘HyperLogLog:Redis中的基数统计黑科技
116 0
|
3月前
|
存储 NoSQL BI
Redis 实战篇:巧用 Bitmap 实现亿级海量数据统计
Redis 实战篇:巧用 Bitmap 实现亿级海量数据统计
52 0
|
3月前
|
存储 NoSQL 关系型数据库
Redis 日志篇:无畏宕机快速恢复的杀手锏
Redis 日志篇:无畏宕机快速恢复的杀手锏
74 0
|
4月前
|
SQL 大数据 API
每天一道大厂SQL题【Day08】服务日志SQL统计
每天一道大厂SQL题【Day08】服务日志SQL统计
42 0
|
15天前
|
缓存 NoSQL 调度
【redis】ssm项目整合redis,redis注解式缓存及应用场景,redis的击穿、穿透、雪崩的解决方案
【redis】ssm项目整合redis,redis注解式缓存及应用场景,redis的击穿、穿透、雪崩的解决方案
106 0
|
1月前
|
存储 JSON NoSQL
Redis与Python的完美结合:实现高效数据交互和应用场景全解析
Redis与Python的完美结合:实现高效数据交互和应用场景全解析
113 0
|
1月前
|
存储 缓存 NoSQL
探索Redis的多样应用场景:加速和优化现代应用
探索Redis的多样应用场景:加速和优化现代应用
33 2
|
2月前
|
存储 运维 NoSQL
Redis统计大法:挖掘数据的四重宝藏【redis第五部分】
Redis统计大法:挖掘数据的四重宝藏【redis第五部分】
70 0

热门文章

最新文章