【高并发解决方案】日志系统优化

直奔主题

我们并不打算把日志写到文件,一个原因是这可能会造成文件阻塞,另一个重要的原因是,高并发场景下部署的服务器一般有几十台上百台,又或者使用了弹性计算,这使得把日志写在服务器的文件系统并不现实。因此很有必要把web请求产生的日志重定向写入独立的数据库,本文以mysql为例。

看看日志的几个特点:

1、数据量巨大 (因此主键一般使用bigint,最大9223372036854775807够你浪了)
2、以写为主、没有事务处理或外键 (数据库引擎建议使用Archive,MyISAM也行)
3、不必等待写入结果 (可以使用INSERT DELAYED,或先落cache)

首先最重要的当然是设计合理的日志表:
CREATE TABLE IF NOT EXISTS `app_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`code` int(10) NOT NULL DEFAULT 99999 COMMENT ‘错误码,1xxxx,2xxxx,3xxxx’,
`msg` varchar(255) NOT NULL COMMENT ‘日志内容’,
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) ENGINE=Archive DEFAULT CHARSET=utf8 COMMENT=’app日志’ AUTO_INCREMENT=1 ;

如果要处理的日志没有亿级,自增id用int就够了。表尽量简洁,产生日志内容时简明扼要,不要产生太多无意义的数据,如果要保存更多字段,可以用json编码后统一保存到一个字段中。使用Archive引擎,除了性能强劲外,它将节省你大量的硬盘空间。如果需要对日志进行比较多的查找和统计,那就使用MyISAM吧。

错误码code的使用:
每条日志一定要对应一个错误码,这方便日后查询和统计,单纯使用error、warn之类的,粒度太大的话,当产生大量的日志时要查找某条日志可能要等上半天,这个坑很多人踩过。
在代码中每个锚点都可以对应一个错误码,这样可以精确定位,方面问题查找。
错误码也可以遵循一定的格式,依需求而定:
例如1xxxx代表严重错误、2xxxx代表警告…
一举两得。

单条插入:
INSERT INTO app_log(`code`,`msg`,`create_time`)
VALUES(20000,’这是一条错误日志。’,’2017-2-16 21:36:45′);

先落缓存再入库时,批量插入性能比单条插入好很多(一般每次插入上百条或者上千条):
INSERT INTO app_log(`code`,`msg`,`create_time`)
VALUES(20000,’这是一条错误日志。’,’2017-2-16 21:36:45′)
,(200,’Can not connect to MySQL server : Too many connections’,’2017-2-16 21:36:55′);

注意:SQL语句有长度限制,在进行语句合并不要超过SQL长度限制,可以修改max_allowed_packet配置合适的值。

批量延迟插入:
INSERT DELAYED app_log(`code`,`msg`,`create_time`)
VALUES(20000,’这是一条错误日志。’,’2017-2-16 21:36:45′)
,(200,’Can not connect to MySQL server : Too many connections’,’2017-2-16 21:36:55′);

为什么还要插入时间戳?
在复杂的环境中,数据库产生的时间戳跟产生这条日志的时间可能相去甚远,如果某些日志的顺序或者时间很敏感,建议不要使用默认时间戳。
把日志写入数据库是方便我们查找和统计,但在高并发场景下,让日志占用有限数据库资源是绝不允许的,因此可以先把日志写入缓存队列,然后异步写入数据库。

这里推荐用redis缓存的队列
先把日志信息的编码成为字符串,存入redis队列,后续的进程在服务器压力不是很大的时候从队列中取出字符串解码后写入数据库。
这里在生产环境中要注意的是日志丢失的问题,缓存是不稳定的,网络也是不稳定的,数据库能否连接成功也是不确定的。
在把日志从缓存中取出来一条日志之后可能连接数据库失败,这条日志便没法写入数据库,这时要不要又把它写回缓存?
实际上在redis中,我们会使用两个队列,一个队列存保存未入库的日志,另一个队列保存已入库的日志。使用redis的rpoplpush能让我们的缓存数据始终留在redis服务器。

在服务器集群中,这就要考虑到hash tag的问题了。
[未完]
【2017-2-16 22:35:46】@SCNU

发表评论

电子邮件地址不会被公开。 必填项已用*标注