Using Application Algorithm to improve Database performance
RWP团队谈性能优化之大开眼界篇(二)应用程序算法
您觉得,应用程序的算法优化与否,最多能对系统性能有多大影响?
A. 20-30%
B. 50%
C. 10倍左右
D. 100倍或更多
E. 没什么大影响吧
处理1行数据1ms,倒是挺快的,但是如果一行一行处理,处理万亿级别的数据的话,就要1ms * 1,000,000,000,000的时间,也就是32年的时间,32年之后处理完了,还能有什么意义呢?
这个计算,告诉我们,当需要处理的数据量很大的时候,用处理少量数据的办法(一行一行处理),就是不合适的了
Row by row(逐行)的数据处理方法,是一个进程处理数据,每次处理一行。对于小的数据集非常适合。处理大量数据的时候,就不适合了。
Arrays(数组)的数据处理方法,也是一个进程处理数据,每次处理一组数据。对于小的数据集非常适合,比row by row的方法效率高,因为减少了数据中网络上往返的时间,commit的时间,和客户端与服务器中的code path。
使用Arrays(数组)的方法,array size设多大比较合适呢?我们进行了demo.
Demo里面有7个选手,每个选手用不同的array size向数据库里面插入数据:
Demo中可以看到,4x选手(Array size = 4),比1x选手(Array size = 1,也就是row by row),处理速度快很多。这是因为Oracle数据库对于array interface是做了特别优化的,array interface和非array interface的code path是不同的
另外也可以看到,array size并不是越大越好。Array size只是定义了在数据库这个层面,每次处理的数据量,但是在客户端和数据库服务器之间,这些数据还要按照网络传输的单元去缓存和传输。所以当array size到达一定大小的时候,array size对性能大小的影响就没那么大了。Demo里面的后三个选手,用的时间都差不多。大家也不用纠结array size=4096好,还是4099好
Row by row和Arrays都是一个进程处理数据。 如果数据量大,系统上又有那么多资源,需要并行才能把资源用起来。现实中很多人的办法是,把原有的row by row或者arrays的代码拿过来,并行执行,这种方法称为手动并行,因为给每个进程分配任务,是用户通过自己写脚本来手动完成的,如果有某个进程在处理数据的过程中出了问题,也需要手动来处理
手动并行的数据处理方法,实施起来很复杂,在多个线程上平均分配工作负载就是个很大的挑战,多个线程对数据库对象执行相同操作时还可能出现争用问题
然后进行了array vs Parallelism的demo。
Demo的任务是向数据库里面插入2000万行数据,Array选手用arrays的方法来完成任务,array size=1024. Parallelism选手用手动并行的方法完成任务,每个Java Thread里面写一个类,里面再写一个方法,这个方法是向数据库里面插入一条数据并提交,然后同时启动128个Java Thread来完成任务,这128个Java Thread通过连接池连到数据库上,连接池大小是24.
结果Array选手胜。
然后手动并行选手增加了并行度,512个Java Thread,通过96个连接连到数据库上。结果会快一点点,但是还是比Array选手慢很多。
然后又给表上加了索引,加了索引之后,Array选手的胜出优势更明显了
做这个demo是因为实际工作中看到很多出现问题的情况,都是用手动并行那个选手的办法来做的。然后说,数据库性能不好,里面有很多等待事件。那么这个对比会不会让正在刷手机的你有个思路上的转变呢?
是数据库性能不好,还是应用程序的算法不好?
向数据库里面插入数据这个任务,一个进程,用array方法,效率满高的。而且因为是一个进程在干活,需要维护索引的时候所涉及的各种等待事件也不存在,因为没有其他进程干扰
所以说,每种数据处理技术都有自己适合的场景,当现实工作中,面对手动并行的选手的问题时候,理解array interface的强大,学会使用array interface,好处大大de
接下来讨论了set based processing,基于集合的处理
Set based processing,是用SQL来定义结果,以集合为单位,数据库可以高效的处理数据,得益于网络上没有数据移动,可以充分使用数据库的处理能力,比如用Hash joins,并行查询和DML,如果使用一体机的话,一体机平衡的CPU和IO能力也可以得以充分发挥
SQL的写法很灵活,set based是一个思路和方法,不同的SQL写法都可以是set based的方法。常见的set based的SQL操作有:- create table as select- insert /*+ append */ select- intersect- minus- exists- not exists- window functions- multi-table inserts
- outer joins
接下来进行了一个demo。模拟一个数据仓库的ETL场景,将一亿行数据加载到数据库里面(Loading),对这些数据进行重复数据删除的处理(De-duplicating),将这些数据进行转换(Transforming),转换为对总部汇总有用的数据,最后进行汇总(Aggregating),也就是完成当天的营业数据分析
Demo中有四个选手,每个选手使用一种方法处理数据,实现的功能完全相同,也就是上面说的当日营业数据分析的功能。
Demo中用到的代码都是PL/SQL写的,原因是做数据库的人大都熟悉。重点是算法,用其他语言实现,最终对比的效果也类似。
接下来进行了demo。Demo中每个阶段处理的数据量都是大概1亿行。
在Demo过程中的四个步骤四个选手的表现都类似,下面是Loading阶段的demo截图。Set based的表现遥遥领先
Set based选手在各个阶段都完胜。set based选手总共只用了不到1分钟,而row by row选手需要十几个小时。性能相差超过1000倍
1000倍的性能提升,意味着从十几个小时到1分钟, 意味着日常工作决策的方式都可以改变,从等着第二天第三天数据跑出来了再决策,到可以即时决策
1000倍的性能提升,靠的是算法的改变。所以说,性能优化,不光是DBA的事儿,更是开发的事儿。如果算法上定了row by row的方法,那么再怎么改,也是在十几个小时,或者几个小时,这样范畴的性能。算法上定的是set based,set based的SQL可以有不同的写法,执行计划可能有差别,但是性能上也是几十秒或者几分钟的范畴。
总结
处理少量数据的时候,一个进程即可,可以考虑row by row的方法,或者arrays的方法。能用arrays方法就用arrays方法。
处理大量数据的时候,需要多个进程同时处理以在短时间内完成,可以考虑手动并行的方法或者set based的方法。能用set based方法就用set based方法。
总之,面对性能问题的时候,要首先整明白面对的是什么样的问题。是处理少量数据的问题,还是处理大量数据的问题,系统里的资源和并发用户情况怎么样,然后选用适合的算法。处理大量数据的时候,算法对于性能的影响,常见百倍千倍,小伙伴们撸起袖子加油干吧~~
Comments
Post a Comment