SysLog & Mysql 个人经验总结和分享

写这篇文章算是对我来淘宝平台架构组实习的一个总结吧。

开始接手SysLog的时候,那时候SysLog是比较粗糙的,一天只有几万条数据,显示图的曲线都是一路往上涨的,数据的值是直接累加,很难看出某个时间点的值,只能从曲线的曲率上大概判断现在是不是增长的很快。开始的时候需求很简单,毕玄只是让只是要让曲线按照每一分钟的实际值显示就行了。

还记得当时改的时候真的是一步三回头,在学校的时候哪有这种经历,自己做的东西马上要给别人用了。那次是查了又查,还是担心。虽然真的就改了一点点东西。这感觉相信每个从学校刚出来的人都会经历的:紧张,激动,兴奋&&担忧。

其实当时的结构还是比较简单直接的。数据库中一个月才一张表,就是原始数据表,那时候一个一天才几万的数据量。

实现方案

入库:

接收到客户端数据  解析直接入库

查看:

获取参数  从原始数据表取数据  处理数据  展示

这时候没学到多少技术,学到最多的是就是debug,其实就debug的步骤没什么好说的:设置断点,单步跟踪,运行到返回,运行到下一个断点更重要的是在debug之前,造成错误结果的可能的原因在哪,先找到原因再下断点。当然肯定会有无从下手的时候,那没办法,从开始就下断点,一步步的跟踪,每一步对比跟是不是预期值,这个要是错过了一步可能就得从头再来,比较恶心。

=================================下面是历史的改进==============================

问题一:如何让用户更快速的查看历史或当前的数据?

当数据量开始增大的时候,从每天几万条增长到几十万条,程序不做优化,mysql有点开始有点吃力了。

记得某天早上,华黎师兄过来跟我说:小伙,现在访问一个页面要10分钟才能刷出来,都不敢刷新啊。我大囧。然后黎叔给我了一些建议,说用缓存吧,以至于我建的表名都是listCachexxx(其实就是把中间结果存起来)

优化一:对已经入库的数据定时的进行计算,将计算结果存放至另外的表中。用户请求数据直接返回计算好的结果

实现方案:

入库:

接收到agent的数据  解析直接入库

更新:

接受更新请求  取出原始数据中需要更新的数据  处理原始数据放入缓存

查看:

获取参数  缓存表取数据  展示数据

优化结果:请求响应的速度比原来的提高了10倍左右。这个其实不仅仅是10倍的概念,随着数据量的不断增长,已经是一个可用性的问题。

=============================================================================

问题二:一个天的数据都在一张表里,有一天某个应用的数据量猛增的话,现在的SysLog的性能跟不上,有什么办法SysLog仍然能继续运行?

处理过程多出来一步更新,请求响应的速度比原来的提高了好几倍。但是问题还是随之而来,记得是因为HC应用某一天的数据量猛增到500w+,当时更新的是把需要更新的数据从数据库一下子都取出来,内存一下子就被撑爆了@_@

这下SysLog是完全不能查看了,一更新就内存溢出。而且因为HC的原因,导致N应用和HP等应用都不能查看数据。

优化二:按照应用,日期分表。按时间段更新,一次更新最多取出n分钟的数据,防止取出数据太多,内存溢出

技术演变:

入库:

按照应用将不同应用,不同的日期的数据写入不同的表中

更新:

更新分时间按段更新,每次更新从原始数据表中最多只会取出n分钟的数据

优化结果:不会因为一个应用的异常而导致所有的应用都不能使用,这是当时数据分表的导火索,后来证明分表的作用不仅仅是这些。

问题三:分时间分表更新,速度会比原来的程序更新的效率比较低,如何补偿?

按时间段更新,对数据库的更新操作会更多一些,效率比先前的低了。

优化三:使用线程池,利用多线程分表更新。

多线程终于出场了。在应用里自己写多线程,在学校里的时候用的不多,心里自然没什么底,只能是走一步算一步了。

不过这次的多线程是很简单的多线程运用,没有线程之间共同需要修改的资源,那这个原理上就跟每个线程System.out.println(“hello taobao”)一样了。不过到底从这是开始引入了多线程(servlet自身的多线程不算)

技术演变:

更新:

每个应用启动一个线程去更新

优化结果:多用多线程分表更新,由于应用与应用之间是没有依赖的,使用多线程后,更新速度反而比以前效率更高了

=============================================================================

终于SysLog暂时稳定了一段时间。这段时间毕玄新提了一些需求,丰富了报表的种类,SysLog提供:日趋势报表,周趋势报表,季度趋势报表,年度趋势报表,历史最高最低对比报表,同期对比报表,分机器查看报表,还有一个日趋势对表报表。

这期间没有什么技术上的变化,基本上是代码结构上的变化和功能的扩展。

值得一提的是在服务端有个变化,数据入库和原始数据的处理分为了两台机器,数据库是单独的一台服务器。在一个tomcat上的时候如果应用刚好在更新,那么查看报表会明显缓慢。分开之后,即使正在更新应用,查看报表的响应也会很快。这个虽然不是难理解的变化,但是对于我这个初出茅庐的实习生还是有一种醍醐灌顶的感觉,原来还可以这么做。

=============================================================================

问题四:数据量继续往上飙,数据入库都成了瓶颈,数据库load很高,如果降低数据库的load

在上面一段时间的安逸之后,真正的挑战渐渐来临。随着数据量的与日递增,数据库服务器的load有了不小的增长。这时候至道师兄加入进来一起优化SysLog了。

优化四:接收到请求暂时不入库,先将数据写内存。N分钟后写数据库

数据库的load变高,主要原因是数据库连接的资源不够。这时候每天的数据量已经达到了1000w了。有请求过来数据直接入库那对数据库连接的争夺必然是很厉害,优化入库就成了势在必行的任务。不过这次的入库优化,过程还是比较曲折的。

这里涉及到高并发控制同步的问题,如何更快速的响应客户端入库的请求?我选择的是ReentrantLock,其实有很多选择可以用,只是我觉得这个用起来比较爽。不过用锁的时候要搞明白lock()tryLock()的区别。tryLock()是立即返回的,意思就是能锁就锁,不能锁就不锁(当然tryLock()还有一个定时的方法,有兴趣可以去看下)。Lock()看方法就是一根经,不等到锁就是不返回。用的时候一定要注意两者的使用环境,搞不好结果就会出乎意料之外。我的感觉是设计方案时能没有并发最好……

ConcurrentHashMapconcurrent包里提供的这个HashMap还是很实用的,所有的操作都是线程安全的,迭代的时候也不会抛出令人烦恼的ConcurrentModificationException,对我这种新手来说,已经是觉得很方便了。比普通的HashMap多出了一个putIfAbsent()方法,可以看下。

此次改变之后,在压测的时候发现有内存泄漏的问题,每次在压测一刻钟左右的时候TPS就开始慢慢减为零。检查了好久的代码,还是没有找到原因,很郁闷,当时。过了好几天才发现是没有流量控制,内存被loadrunner的数据给撑满了。不过这期间,我学到了不少关于内存泄漏的定位工具:JProfilerJDK自带的jmap,jhat的用法(这个很强大,详情请见贴吧之大话架构http://soft.taobao.ali.com/Post/ShowTopic.aspx?t=2d8396b5-1201-4790-b0ee-145ebc28e2c3)

技术演变:

入库:

接收到请求暂时不入库,先将数据写内存。N分钟后写数据库

优化结果:n分钟一次入库请求,数据库的load明显的降低了很多。

问题五:在修改入库策略后,n分钟只有一次请求写数据库,数据入库的速率有下降的迹象,如何提高入库速度?

修改之后数据库load降下来很多,但是入库的速度成了瓶颈。原因在于n分钟才有一个连接去写数据库,比之前的多连接写肯定慢了一些。

优化五:多线程入库。使用可控数量的线程执行入库动作,每个应用分得一个线程去入库。

分表的强大的优势开始体现出来。入库任务启动时,每个应用用一个线程去写数据库。由于是按照应用分表的,各线程之间插入数据是没有锁的。效率一下子提高了狠多。

Mysql引擎:我用过的两个引擎一个是MYISAM,一个是InnoDBSysLog数据库的效率是至关重要的环节,所以数据表引擎的选择毋庸置疑的很重要。

MYISAM特点是:不支持事务,执行效率相对比较高,对并发的支持不是很好,读写都是串行的,只有读读锁是兼容的,其他的读写锁不兼容,写写锁不兼容,不支持行锁,要锁直接锁表。

InnoDB特点是:支持事务,支持行锁,对并发支持的比较好。处理大容量的数据时性能能达到最大化,CPU的利用是最有效率的。类似于oracle的数据库

技术演变:

入库:

数据由内存写入数据库的时候,多线程入库。

优化结果:数据库入库的速度能达到3000+/秒的速度,比原先提高了有5倍左右的样子。

问题六:按照时间段更新的策略,一直有一个不是很严重的bug:更新的任务可能会漏掉其中的一些数据。

原来的更新是按当前时间作为取数据的条件:如果数据的时间在上一次更新至当前时间之间,那么认为这部分数据是新数据。这样其实有个问题,因为数据入库的延时是未知的,按照这种更新的方法可能是会漏掉一些数据。

优化六:按照原始数据的dataId来更新,保证不会丢数据。

这次修改遇到的困难,更多的是压力。因为修改了这个之后,把一个有问题的版本发上去了,发现总是会内存耗尽,导致SysLog好久不能使用。修改这里最多的还是更DBA的沟通,感谢一直被我骚扰的陶方。

Mysql的索引:只要索引的最左字段被包含在查询语句里面,就会走索引,否则是不会走索引的。例如很简单的一个建立了索引key(a,b,c),查询语句会有索引的情况只有三种,即where a = ? , where a = ? and b = ? where a = ? and b = ? and c = ?其他的例如where b = ? and c = ? 是不会走索引的。

还有一个平时看起来没什么用,但是出问题的时候很有用的技巧:打好日志。线上的应用出了问题,不可能给debug的机会。开始总是内存耗尽的状况,我线下怎么也压不出来,后来经毕玄师兄提醒,才通过打日志发现了问题。只打有用的日志,是建立在对程序理解很深刻的情况下。第一次发现打日志的重要性,不仅是手段也是技术……

实现方案:

更新:

按照原始数据的dataId来更新,保证不会丢数据。

优化结果:这次更新优化后,线下测试的效率比修改之前提高了4倍,但是线上超大的数据量的更新还是不进理想的,尤其是key很多的应用,更新的速度还是比较缓慢的。如何更高效的更新,仍旧需要继续思考

问题七:入库的速度按照DBA的说法一张表可以压到1w/秒,为什么我的只有2k -3k?

优化七:一条sql插入多个值。

继续的插入优化,插入经过上次的优化之后,又开始了下一轮的优化。这次着重在如何提高数据库的写入速度。开始我们为了加快插入速度,使用的是iBATIS的批量提交,从理论上讲应该会比一条条的插入快,因该是常识吧。但是这次事实上告诉我,仅仅是把批量提交去掉就已经提高了3倍多,更牛B的在后面。

陶方建议一个sql插入多个值,形如insert into tableName (x,xx,xxx) values(x,xx,xxx),(xx,xxx,xxxx)…,经过这样修改之后,在我看来,速度已经很疯狂了.

优化结果:线上曾经粗略的跟踪过入库速度:可以达到70000w/,捕捉到的可能不是最高峰。这种速度对线上的现在的压力足够支撑了.

( http://dev.mysql.com/doc/refman/5.0/en/insert-speed.html这个文档讲如何提高mysql的插入速度,很不错)

SysLog其实还有很多地方可以优化:如何支撑应用的水平扩展?如何提高更新的计算速度?期待全网监控小组能把SysLog做的更好。

=======================================特别感谢===============================

华黎,毕玄,至道,天狐,平台架构的师兄们,给我传授知识.让我眼界大开

还要特别感谢不厌其烦被我骚扰的DBA:陶方.

=======================================全篇终=================================

你可能感兴趣的:(SysLog & Mysql 个人经验总结和分享)