精华内容
下载资源
问答
  • SQL优化最干货总结 - MySQL(2020最新版)

    万次阅读 多人点赞 2020-06-29 16:55:47
    MySQL - SQL优化干货总结(吐血版),别辜负了自己的梦想,欢迎白嫖、点赞、收藏。

    小伙伴想精准查找自己想看的MySQL文章?喏 → MySQL专栏目录 | 点击这里

    前言

    BATJTMD等大厂的面试难度越来越高,但无论从大厂还是到小公司,一直未变的一个重点就是对SQL优化经验的考察。一提到数据库,先“说一说你对SQL优化的见解吧?”。SQL优化已经成为衡量程序猿优秀与否的硬性指标,甚至在各大厂招聘岗位职能上都有明码标注,如果是你,在这个问题上能吊打面试官还是会被吊打呢?

    (注:如果看着模糊,可能是你撸多了)

    目录

    前言

    SELECT语句 - 语法顺序:

    SELECT语句 - 执行顺序:

    SQL优化策略

    一、避免不走索引的场景

    二、SELECT语句其他优化

    三、增删改 DML 语句优化

    四、查询条件优化

    五、建表优化

    一张照片背后的故事(自娱角)


    有朋友疑问到,SQL优化真的有这么重要么?如下图所示,SQL优化在提升系统性能中是:(成本最低 && 优化效果最明显) 的途径。如果你的团队在SQL优化这方面搞得很优秀,对你们整个大型系统可用性方面无疑是一个质的跨越,真的能让你们老板省下不止几沓子钱。

    • 优化成本:硬件>系统配置>数据库表结构>SQL及索引。
    • 优化效果:硬件<系统配置<数据库表结构<SQL及索引。
    String result = "嗯,不错,";
    
    if ("SQL优化经验足") {
        if ("熟悉事务锁") {
            if ("并发场景处理666") {
                if ("会打王者荣耀") {
                    result += "明天入职" 
                }
            }
        }
    } else {
        result += "先回去等消息吧";
    } 
    
    Logger.info("面试官:" + result );

    别看了,上面这是一道送命题。

    好了我们言归正传,首先,对于MySQL层优化我一般遵从五个原则:

    1. 减少数据访问: 设置合理的字段类型,启用压缩,通过索引访问等减少磁盘IO
    2. 返回更少的数据: 只返回需要的字段和数据分页处理 减少磁盘io及网络io
    3. 减少交互次数: 批量DML操作,函数存储等减少数据连接次数
    4. 减少服务器CPU开销: 尽量减少数据库排序操作以及全表查询,减少cpu 内存占用
    5. 利用更多资源: 使用表分区,可以增加并行操作,更大限度利用cpu资源

    总结到SQL优化中,就三点:

    • 最大化利用索引;
    • 尽可能避免全表扫描;
    • 减少无效数据的查询;

    理解SQL优化原理 ,首先要搞清楚SQL执行顺序:

    SELECT语句 - 语法顺序:

    1. SELECT 
    2. DISTINCT <select_list>
    3. FROM <left_table>
    4. <join_type> JOIN <right_table>
    5. ON <join_condition>
    6. WHERE <where_condition>
    7. GROUP BY <group_by_list>
    8. HAVING <having_condition>
    9. ORDER BY <order_by_condition>
    10.LIMIT <limit_number>


     

    SELECT语句 - 执行顺序:

    FROM
    <表名> # 选取表,将多个表数据通过笛卡尔积变成一个表。
    ON
    <筛选条件> # 对笛卡尔积的虚表进行筛选
    JOIN <join, left join, right join...> 
    <join表> # 指定join,用于添加数据到on之后的虚表中,例如left join会将左表的剩余数据添加到虚表中
    WHERE
    <where条件> # 对上述虚表进行筛选
    GROUP BY
    <分组条件> # 分组
    <SUM()等聚合函数> # 用于having子句进行判断,在书写上这类聚合函数是写在having判断里面的
    HAVING
    <分组筛选> # 对分组后的结果进行聚合筛选
    SELECT
    <返回数据列表> # 返回的单列必须在group by子句中,聚合函数除外
    DISTINCT
    # 数据除重
    ORDER BY
    <排序条件> # 排序
    LIMIT
    <行数限制>


    SQL优化策略

    声明:以下SQL优化策略适用于数据量较大的场景下,如果数据量较小,没必要以此为准,以免画蛇添足。


    一、避免不走索引的场景

    1. 尽量避免在字段开头模糊查询,会导致数据库引擎放弃索引进行全表扫描。如下:

    SELECT * FROM t WHERE username LIKE '%陈%'

    优化方式:尽量在字段后面使用模糊查询。如下:

    SELECT * FROM t WHERE username LIKE '陈%'

    如果需求是要在前面使用模糊查询,

    • 使用MySQL内置函数INSTR(str,substr) 来匹配,作用类似于java中的indexOf(),查询字符串出现的角标位置,可参阅《MySQL模糊查询用法大全(正则、通配符、内置函数等)》
    • 使用FullText全文索引,用match against 检索
    • 数据量较大的情况,建议引用ElasticSearch、solr,亿级数据量检索速度秒级
    • 当表数据量较少(几千条儿那种),别整花里胡哨的,直接用like '%xx%'。

     

    2. 尽量避免使用in 和not in,会导致引擎走全表扫描。如下:

    SELECT * FROM t WHERE id IN (2,3)

    优化方式:如果是连续数值,可以用between代替。如下:

    SELECT * FROM t WHERE id BETWEEN 2 AND 3

    如果是子查询,可以用exists代替。详情见《MySql中如何用exists代替in》如下:

    -- 不走索引
    select * from A where A.id in (select id from B);
    -- 走索引
    select * from A where exists (select * from B where B.id = A.id);


     
    3. 尽量避免使用 or,会导致数据库引擎放弃索引进行全表扫描。如下:

    SELECT * FROM t WHERE id = 1 OR id = 3

    优化方式:可以用union代替or。如下:

    SELECT * FROM t WHERE id = 1
       UNION
    SELECT * FROM t WHERE id = 3

    4. 尽量避免进行null值的判断,会导致数据库引擎放弃索引进行全表扫描。如下:

    SELECT * FROM t WHERE score IS NULL

    优化方式:可以给字段添加默认值0,对0值进行判断。如下:

    SELECT * FROM t WHERE score = 0

     

    5.尽量避免在where条件中等号的左侧进行表达式、函数操作,会导致数据库引擎放弃索引进行全表扫描。

    可以将表达式、函数操作移动到等号右侧。如下:

    -- 全表扫描
    SELECT * FROM T WHERE score/10 = 9
    -- 走索引
    SELECT * FROM T WHERE score = 10*9

     

    6. 当数据量大时,避免使用where 1=1的条件。通常为了方便拼装查询条件,我们会默认使用该条件,数据库引擎会放弃索引进行全表扫描。如下:

    SELECT username, age, sex FROM T WHERE 1=1

    优化方式:用代码拼装sql时进行判断,没 where 条件就去掉 where,有where条件就加 and。


    7. 查询条件不能用 <> 或者 !=

    使用索引列作为条件进行查询时,需要避免使用<>或者!=等判断条件。如确实业务需要,使用到不等于符号,需要在重新评估索引建立,避免在此字段上建立索引,改由查询条件中其他索引字段代替。


    8. where条件仅包含复合索引非前置列

    如下:复合(联合)索引包含key_part1,key_part2,key_part3三列,但SQL语句没有包含索引前置列"key_part1",按照MySQL联合索引的最左匹配原则,不会走联合索引。详情参考《联合索引的使用原理》

    select col1 from table where key_part2=1 and key_part3=2


    9. 隐式类型转换造成不使用索引 

    如下SQL语句由于索引对列类型为varchar,但给定的值为数值,涉及隐式类型转换,造成不能正确走索引。 

    select col1 from table where col_varchar=123; 


    10. order by 条件要与where中条件一致,否则order by不会利用索引进行排序

    -- 不走age索引
    SELECT * FROM t order by age;
    
    -- 走age索引
    SELECT * FROM t where age > 0 order by age;

    对于上面的语句,数据库的处理顺序是:

    • 第一步:根据where条件和统计信息生成执行计划,得到数据。
    • 第二步:将得到的数据排序。当执行处理数据(order by)时,数据库会先查看第一步的执行计划,看order by 的字段是否在执行计划中利用了索引。如果是,则可以利用索引顺序而直接取得已经排好序的数据。如果不是,则重新进行排序操作。
    • 第三步:返回排序后的数据。

    当order by 中的字段出现在where条件中时,才会利用索引而不再二次排序,更准确的说,order by 中的字段在执行计划中利用了索引时,不用排序操作。

    这个结论不仅对order by有效,对其他需要排序的操作也有效。比如group by 、union 、distinct等。

    11. 正确使用hint优化语句

    MySQL中可以使用hint指定优化器在执行时选择或忽略特定的索引。一般而言,处于版本变更带来的表结构索引变化,更建议避免使用hint,而是通过Analyze table多收集统计信息。但在特定场合下,指定hint可以排除其他索引干扰而指定更优的执行计划。

    1. USE INDEX 在你查询语句中表名的后面,添加 USE INDEX 来提供希望 MySQL 去参考的索引列表,就可以让 MySQL 不再考虑其他可用的索引。例子: SELECT col1 FROM table USE INDEX (mod_time, name)...
    2. IGNORE INDEX 如果只是单纯的想让 MySQL 忽略一个或者多个索引,可以使用 IGNORE INDEX 作为 Hint。例子: SELECT col1 FROM table IGNORE INDEX (priority) ...
    3. FORCE INDEX 为强制 MySQL 使用一个特定的索引,可在查询中使用FORCE INDEX 作为Hint。例子: SELECT col1 FROM table FORCE INDEX (mod_time) ...

    在查询的时候,数据库系统会自动分析查询语句,并选择一个最合适的索引。但是很多时候,数据库系统的查询优化器并不一定总是能使用最优索引。如果我们知道如何选择索引,可以使用FORCE INDEX强制查询使用指定的索引。《MySQL中特别实用的几种SQL语句送给大家》博文建议阅读,干货

    例如:

    SELECT * FROM students FORCE INDEX (idx_class_id) WHERE class_id = 1 ORDER BY id DESC;

     

    二、SELECT语句其他优化

    1. 避免出现select *

    首先,select * 操作在任何类型数据库中都不是一个好的SQL编写习惯。

    使用select * 取出全部列,会让优化器无法完成索引覆盖扫描这类优化,会影响优化器对执行计划的选择,也会增加网络带宽消耗,更会带来额外的I/O,内存和CPU消耗。

    建议提出业务实际需要的列数,将指定列名以取代select *。具体详情见《为什么大家都说SELECT * 效率低》


    2. 避免出现不确定结果的函数

    特定针对主从复制这类业务场景。由于原理上从库复制的是主库执行的语句,使用如now()、rand()、sysdate()、current_user()等不确定结果的函数很容易导致主库与从库相应的数据不一致。另外不确定值的函数,产生的SQL语句无法利用query cache。


    3.多表关联查询时,小表在前,大表在后。

    在MySQL中,执行 from 后的表关联查询是从左往右执行的(Oracle相反),第一张表会涉及到全表扫描,所以将小表放在前面,先扫小表,扫描快效率较高,在扫描后面的大表,或许只扫描大表的前100行就符合返回条件并return了。

    例如:表1有50条数据,表2有30亿条数据;如果全表扫描表2,你品,那就先去吃个饭再说吧是吧。

    4. 使用表的别名

    当在SQL语句中连接多个表时,请使用表的别名并把别名前缀于每个列名上。这样就可以减少解析的时间并减少哪些友列名歧义引起的语法错误。


    5. 用where字句替换HAVING字句

    避免使用HAVING字句,因为HAVING只会在检索出所有记录之后才对结果集进行过滤,而where则是在聚合前刷选记录,如果能通过where字句限制记录的数目,那就能减少这方面的开销。HAVING中的条件一般用于聚合函数的过滤,除此之外,应该将条件写在where字句中。

    where和having的区别:where后面不能使用组函数

    6.调整Where字句中的连接顺序

    MySQL采用从左往右,自上而下的顺序解析where子句。根据这个原理,应将过滤数据多的条件往前放,最快速度缩小结果集。


    三、增删改 DML 语句优化

    1. 大批量插入数据

    如果同时执行大量的插入,建议使用多个值的INSERT语句(方法二)。这比使用分开INSERT语句快(方法一),一般情况下批量插入效率有几倍的差别。

    方法一:

    insert into T values(1,2); 
    
    insert into T values(1,3); 
    
    insert into T values(1,4);

    方法二:

    Insert into T values(1,2),(1,3),(1,4); 


    选择后一种方法的原因有三。 

    • 减少SQL语句解析的操作,MySQL没有类似Oracle的share pool,采用方法二,只需要解析一次就能进行数据的插入操作;
    • 在特定场景可以减少对DB连接次数
    • SQL语句较短,可以减少网络传输的IO。

    2. 适当使用commit

    适当使用commit可以释放事务占用的资源而减少消耗,commit后能释放的资源如下:

    • 事务占用的undo数据块;
    • 事务在redo log中记录的数据块; 
    • 释放事务施加的,减少锁争用影响性能。特别是在需要使用delete删除大量数据的时候,必须分解删除量并定期commit。


    3. 避免重复查询更新的数据

    针对业务中经常出现的更新行同时又希望获得改行信息的需求,MySQL并不支持PostgreSQL那样的UPDATE RETURNING语法,在MySQL中可以通过变量实现。

    例如,更新一行记录的时间戳,同时希望查询当前记录中存放的时间戳是什么,简单方法实现:

    Update t1 set time=now() where col1=1; 
    
    Select time from t1 where id =1; 

    使用变量,可以重写为以下方式: 

    Update t1 set time=now () where col1=1 and @now: = now (); 
    
    Select @now; 

    前后二者都需要两次网络来回,但使用变量避免了再次访问数据表,特别是当t1表数据量较大时,后者比前者快很多。


    4.查询优先还是更新(insert、update、delete)优先

    MySQL 还允许改变语句调度的优先级,它可以使来自多个客户端的查询更好地协作,这样单个客户端就不会由于锁定而等待很长时间。改变优先级还可以确保特定类型的查询被处理得更快。我们首先应该确定应用的类型,判断应用是以查询为主还是以更新为主的,是确保查询效率还是确保更新的效率,决定是查询优先还是更新优先。下面我们提到的改变调度策略的方法主要是针对只存在表锁的存储引擎,比如 MyISAM 、MEMROY、MERGE,对于Innodb 存储引擎,语句的执行是由获得行锁的顺序决定的。MySQL 的默认的调度策略可用总结如下:

    1)写入操作优先于读取操作。

    2)对某张数据表的写入操作某一时刻只能发生一次,写入请求按照它们到达的次序来处理。

    3)对某张数据表的多个读取操作可以同时地进行。MySQL 提供了几个语句调节符,允许你修改它的调度策略:

    • LOW_PRIORITY关键字应用于DELETE、INSERT、LOAD DATA、REPLACE和UPDATE;
    • HIGH_PRIORITY关键字应用于SELECT和INSERT语句;
    • DELAYED关键字应用于INSERT和REPLACE语句。


           如果写入操作是一个 LOW_PRIORITY(低优先级)请求,那么系统就不会认为它的优先级高于读取操作。在这种情况下,如果写入者在等待的时候,第二个读取者到达了,那么就允许第二个读取者插到写入者之前。只有在没有其它的读取者的时候,才允许写入者开始操作。这种调度修改可能存在 LOW_PRIORITY写入操作永远被阻塞的情况。

    SELECT 查询的HIGH_PRIORITY(高优先级)关键字也类似。它允许SELECT 插入正在等待的写入操作之前,即使在正常情况下写入操作的优先级更高。另外一种影响是,高优先级的 SELECT 在正常的 SELECT 语句之前执行,因为这些语句会被写入操作阻塞。如果希望所有支持LOW_PRIORITY 选项的语句都默认地按照低优先级来处理,那么 请使用--low-priority-updates 选项来启动服务器。通过使用 INSERTHIGH_PRIORITY 来把 INSERT 语句提高到正常的写入优先级,可以消除该选项对单个INSERT语句的影响。


    四、查询条件优化

    1. 对于复杂的查询,可以使用中间临时表 暂存数据;


    2. 优化group by语句

    默认情况下,MySQL 会对GROUP BY分组的所有值进行排序,如 “GROUP BY col1,col2,....;” 查询的方法如同在查询中指定 “ORDER BY col1,col2,...;” 如果显式包括一个包含相同的列的 ORDER BY子句,MySQL 可以毫不减速地对它进行优化,尽管仍然进行排序。

    因此,如果查询包括 GROUP BY 但你并不想对分组的值进行排序,你可以指定 ORDER BY NULL禁止排序。例如:

    SELECT col1, col2, COUNT(*) FROM table GROUP BY col1, col2 ORDER BY NULL ;

    3. 优化join语句

    MySQL中可以通过子查询来使用 SELECT 语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的 SQL 操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询可以被更有效率的连接(JOIN)..替代。


    例子:假设要将所有没有订单记录的用户取出来,可以用下面这个查询完成:

    SELECT col1 FROM customerinfo WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo )


    如果使用连接(JOIN).. 来完成这个查询工作,速度将会有所提升。尤其是当 salesinfo表中对 CustomerID 建有索引的话,性能将会更好,查询如下:

    SELECT col1 FROM customerinfo 
       LEFT JOIN salesinfoON customerinfo.CustomerID=salesinfo.CustomerID 
          WHERE salesinfo.CustomerID IS NULL 

    连接(JOIN).. 之所以更有效率一些,是因为 MySQL 不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。


    4. 优化union查询

    MySQL通过创建并填充临时表的方式来执行union查询。除非确实要消除重复的行,否则建议使用union all。原因在于如果没有all这个关键词,MySQL会给临时表加上distinct选项,这会导致对整个临时表的数据做唯一性校验,这样做的消耗相当高。

    高效:

    SELECT COL1, COL2, COL3 FROM TABLE WHERE COL1 = 10 
    
    UNION ALL 
    
    SELECT COL1, COL2, COL3 FROM TABLE WHERE COL3= 'TEST'; 

    低效:

    SELECT COL1, COL2, COL3 FROM TABLE WHERE COL1 = 10 
    
    UNION 
    
    SELECT COL1, COL2, COL3 FROM TABLE WHERE COL3= 'TEST';

    5.拆分复杂SQL为多个小SQL,避免大事务

    • 简单的SQL容易使用到MySQL的QUERY CACHE; 
    • 减少锁表时间特别是使用MyISAM存储引擎的表; 
    • 可以使用多核CPU。

    6. 使用truncate代替delete

    当删除全表中记录时,使用delete语句的操作会被记录到undo块中,删除记录也记录binlog,当确认需要删除全表时,会产生很大量的binlog并占用大量的undo数据块,此时既没有很好的效率也占用了大量的资源。

    使用truncate替代,不会记录可恢复的信息,数据不能被恢复。也因此使用truncate操作有其极少的资源占用与极快的时间。另外,使用truncate可以回收表的水位,使自增字段值归零。

    7. 使用合理的分页方式以提高分页效率

    使用合理的分页方式以提高分页效率 针对展现等分页需求,合适的分页方式能够提高分页的效率。

    案例1: 

    select * from t where thread_id = 10000 and deleted = 0 
       order by gmt_create asc limit 0, 15;


           上述例子通过一次性根据过滤条件取出所有字段进行排序返回。数据访问开销=索引IO+索引全部记录结果对应的表数据IO。因此,该种写法越翻到后面执行效率越差,时间越长,尤其表数据量很大的时候。

    适用场景:当中间结果集很小(10000行以下)或者查询条件复杂(指涉及多个不同查询字段或者多表连接)时适用。


    案例2: 

    select t.* from (select id from t where thread_id = 10000 and deleted = 0
       order by gmt_create asc limit 0, 15) a, t 
          where a.id = t.id; 

    上述例子必须满足t表主键是id列,且有覆盖索引secondary key:(thread_id, deleted, gmt_create)。通过先根据过滤条件利用覆盖索引取出主键id进行排序,再进行join操作取出其他字段。数据访问开销=索引IO+索引分页后结果(例子中是15行)对应的表数据IO。因此,该写法每次翻页消耗的资源和时间都基本相同,就像翻第一页一样。

    适用场景:当查询和排序字段(即where子句和order by子句涉及的字段)有对应覆盖索引时,且中间结果集很大的情况时适用。

    五、建表优化

    1. 在表中建立索引,优先考虑where、order by使用到的字段。


    2. 尽量使用数字型字段(如性别,男:1 女:2),若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。
    这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。


    3. 查询数据量大的表 会造成查询缓慢。主要的原因是扫描行数过多。这个时候可以通过程序,分段分页进行查询,循环遍历,将结果合并处理进行展示。要查询100000到100050的数据,如下:

    SELECT * FROM (SELECT ROW_NUMBER() OVER(ORDER BY ID ASC) AS rowid,* 
       FROM infoTab)t WHERE t.rowid > 100000 AND t.rowid <= 100050

    4. 用varchar/nvarchar 代替 char/nchar

    尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
    不要以为 NULL 不需要空间,比如:char(100) 型,在字段建立时,空间就固定了, 不管是否插入值(NULL也包含在内),都是占用 100个字符的空间的,如果是varchar这样的变长字段, null 不占用空间。

     

    一张照片背后的故事(自娱角)

     

    这是由蒋玉树先生所拍摄的照片《小店》
    在四川凉山
    母亲临时有事
    让自己的女儿帮忙看店

    突然天上下起了雪
    在这个只有一面墙的小店里
    女孩儿一边搓着手
    一边欣赏着美景

     

    这些MySQL文章你可能也会喜欢:

    《MySQL中的 utf8 并不是真正的UTF-8编码 ! !》

    《MySQL中特别实用的几种SQL语句送给大家》

    《SQL 查询语句先执行 SELECT?兄弟你认真的么?》

    《有意思,原来SQL中的NULL是这么回事儿》

     

    展开全文
  • 优化程序性能总结

    千次阅读 2015-10-23 14:34:57
    系统层次关注系统的控制流程和数据流程优化主要考虑如何减少消息传递的个数;如何使系统的负载更加均衡;如何充分利用硬件的性能和设施;如何减少系统额外开销(比如上下文切换等)。 算法层次关注算法的选择(用...

    性能优化有三个层次:

    系统层次

    算法层次

    代码层次

    系统层次关注系统的控制流程和数据流程,优化主要考虑如何减少消息传递的个数;如何使系统的负载更加均衡;如何充分利用硬件的性能和设施;如何减少系统额外开销(比如上下文切换等)。

    算法层次关注算法的选择(用更高效的算法替换现有算法,而不改变其接口);现有算法的优化(时间和空间的优化);并发和锁的优化(增加任务的并行性,减小锁的开销);数据结构的设计(比如lock-free的数据结构和算法)。

    代码层次关注代码优化,主要是cache相关的优化(I-cache,D-cache相关的优化);代码执行顺序的调整;编译优化选项;语言相关的优化技巧等等。

     

    性能优化需要相关的工具支持,这些工具包括编译器的支持;CPU的支持;以及集成到代码里面的测量工具等等。这些工具主要目的是测量代码的执行时间以及相关的cache miss, cache hit等数据,这些工具可以帮助开发者定位和分析问题。

    性能优化和性能设计不同。性能设计贯穿于设计,编码,测试的整个环节,是产品生命周期的第一个阶段;而性能优化,通常是在现有系统和代码基础上所做的改进,属于产品生命周期的后续几个阶段(假设产品有多个生命周期)。性能优化不是重新设计,性能优化是以现有的产品和代码为基础的,而不是推倒重来。性能优化的方法和技巧可以指导性能设计,但两者的方法和技巧不能等同。两者关注的对象不同。性能设计是从正向考虑问题:如何设计出高效,高性能的系统;而性能优化是从反向考虑问题:在出现性能问题时,如何定位和优化性能。性能设计考验的是开发者正向建设的能力,而性能优化考验的是开发者反向修复的能力。两者可以互补。

     

     

    下面是一个代码优化技巧列表,需要不断地补充,优化和筛选。

    1) Code adjacency (把相关代码放在一起),推荐指数:5颗星

    把相关代码放在一起有两个涵义,一是相关的源文件要放在一起;二是相关的函数在object文件里面,也应该是相邻的。这样,在可执行文件被加载到内存里面的时候,函数的位置也是相邻的。相邻的函数,冲突的几率比较小。而且相关的函数放在一起,也符合模块化编程的要求:那就是  高内聚,低耦合。

    如果能够把一个codepath上的函数编译到一起(需要编译器支持,把相关函数编译到一起),  很显然会提高I-cache的命中率,减少冲突。但是一个系统有很多个code path,所以不可能面面俱到。不同的性能指标,在优化的时候可能是冲突的。所以尽量做对所以case都有效的优化,虽然做到这一点比较难。

    2) Cache line alignment (cache对齐),推荐指数:4颗星

    数据跨越两个cacheline,就意味着两次load或者两次store。如果数据结构是cacheline对齐的,就有可能减少一次读写。数据结构的首地址cache line对齐,意味着可能有内存浪费(特别是数组这样连续分配的数据结构),所以需要在空间和时间两方面权衡。

    3) Branch prediction (分支预测),推荐指数:3颗星(不推荐静态分支预测)

    代码在内存里面是顺序排列的。对于分支程序来说,如果分支语句之后的代码有更大的执行几率,那么就可以减少跳转,一般CPU都有指令预取功能,这样可以提高指令预取命中的几率。分支预测用的就是likely/unlikely这样的宏,一般需要编译器的支持,这样做是静态的分支预测。现在也有很多CPU支持在CPU内部保存执行过的分支指令的结果(分支指令的cache),所以静态的分支预测就没有太多的意义。如果分支是有意义的,那么说明任何分支都会执行到,所以在特定情况下,静态分支预测的结果并没有多好,而且likely/unlikely对代码有很大的侵害(影响可读性),所以一般不推荐使用这个方法。

    4) Data prefetch (数据预取),推荐指数:4颗星

    指令预取是CPU自动完成的,但是数据预取就是一个有技术含量的工作。数据预取的依据是预取的数据马上会用到,这个应该符合空间局部性(spatial locality),但是如何知道预取的数据会被用到,这个要看上下文的关系。一般来说,数据预取在循环里面用的比较多,因为循环是最符合空间局部性的代码。

    但是数据预取的代码本身对程序是有侵害的(影响美观和可读性),而且优化效果不一定很明显(命中的概率)。数据预取可以填充流水线,避免访问内存的等待,还是有一定的好处的。

    5) Memory coloring (内存着色),推荐指数:不推荐

    内存着色属于系统层次的优化,在代码优化阶段去考虑内存着色,有点太晚了。所以这个话题可以放到系统层次优化里面去讨论。

    6)Register parameters (寄存器参数),推荐指数:4颗星

    寄存器做为速度最快的内存单元,不好好利用实在是浪费。但是,怎么用?一般来说,函数调用的参数少于某个数,比如3,参数是通过寄存器传递的(这个要看ABI的约定)。所以,写函数的时候,不要带那么多参数。c语言里还有一个register关键词,不过通常都没什么用处(没试过,不知道效果,不过可以反汇编看看具体的指令,估计是和编译器相关)。尝试从寄存器里面读取数据,而不是内存

    7) Lazy computation (延迟计算),推荐指数:5颗星

    延迟计算的意思是最近用不上的变量,就不要去初始化。通常来说,在函数开始就会初始化很多数据,但是这些数据在函数执行过程中并没有用到(比如一个分支判断,就退出了函数),那么这些动作就是浪费了。

    变量初始化是一个好的编程习惯,但是在性能优化的时候,有可能就是一个多余的动作,需要综合考虑函数的各个分支,做出决定。

    延迟计算也可以是系统层次的优化,比如COW(copy-on-write)就是在fork子进程的时候,并没有复制父进程所有的页表,而是只复制指令部分。当有写发生的时候,再复制数据部分,这样可以避免不必要的复制,提供进程创建的速度。

    8] Early computation (提前计算),推荐指数:5颗星

    有些变量,需要计算一次,多次使用的时候。最好是提前计算一下,保存结果,以后再引用,避免每次都重新计算一次。函数多了,有时就会忽略这个函数都做了些什么,写程序的人可以不了解,但是优化的时候不能不了解。能使用常数的地方,尽量使用常数,加减乘除都会消耗CPU的指令,不可不查。

    9)Inline or not inline (inline函数),推荐指数:5颗星

    Inline or not inline,这是个问题。Inline可以减少函数调用的开销(入栈,出栈的操作),但是inline也有可能造成大量的重复代码,使得代码的体积变大。Inline对debug也有坏处(汇编和语言对不上)。所以用这个的时候要谨慎。小的函数(小于10行),可以尝试用inline;调用次数多的或者很长的函数,尽量不要用inline。

    10) Macro or not macro (宏定义或者宏函数),推荐指数:5颗星

    Macro和inline带来的好处,坏处是一样的。但我的感觉是,可以用宏定义,不要用宏函数。用宏写函数,会有很多潜在的危险。宏要简单,精炼,最好是不要用。中看不中用。

    11) Allocation on stack (局部变量),推荐指数:5颗星

    如果每次都要在栈上分配一个1K大小的变量,这个代价是不是太大了哪?如果这个变量还需要初始化(因为值是随机的),那是不是更浪费了。全局变量好的一点是不需要反复的重建,销毁;而局部变量就有这个坏处。所以避免在栈上使用数组等变量

    12) Multiple conditions (多个条件的判断语句),推荐指数:3颗星

    多个条件判断时,是一个逐步缩小范围的过程。条件的先后,决定了前面的判断是否多余的。根据code path  的情况和条件分支的几率,调整条件的顺序,可以在一定程度上减少code path的开销。但是这个工作做起来有点难度,所以通常不推荐使用。

    13) Per-cpu data structure (非共享的数据结构),推荐指数:5颗星

    Per-cpu data structure 在多核,多CPU或者多线程编程里面一个通用的技巧。使用Per-cpu datastructure的目的是避免共享变量的锁,使得每个CPU可以独立访问数据而与其他CPU无关。坏处是会消耗大量的内存,而且并不是所有的变量都可以per-cpu化。并行是多核编程追求的目标,而串行化是多核编程里面最大的伤害。有关并行和串行的话题,在系统层次优化里面还会提到。

    局部变量肯定是threadlocal的,所以在多核编程里面,局部变量反而更有好处

    14) 64 bits counter in 32 bits environment (32位环境里的64位counter),推荐指数:5颗星

    32位环境里面用64位counter很显然会影响性能,所以除非必要,最好别用。有关counter的优化可以多说几句。counter是必须的,但是还需要慎重的选择,避免重复的计数。关键路径上的counter可以使用per-cpu counter,非关键路径(exception path)就可以省一点内存。

    15) Reduce call path or call trace (减少函数调用的层次),推荐指数:4颗星

    函数越多,有用的事情做的就越少(函数的入栈,出栈等)。所以要减少函数的调用层次。但是不应该破坏程序的美观和可读性。个人认为好程序的首要标准就是美观和可读性。不好看的程序读起来影响心情。所以需要权衡利弊,不能一个程序就一个函数。

    16) Move exception path out (把exception处理放到另一个函数里面),推荐指数:5颗星

    把exceptionpath和critical path放到一起(代码混合在一起),就会影响critical path的cache性能。而很多时候,exception path都是长篇大论,有点喧宾夺主的感觉。如果能criticalpathexception path完全分离开,这样对i-cache有很大帮助。

    17) Read, write split (读写分离),推荐指数:5颗星

    在cache.pdf里面提到了伪共享(false sharing),就是说两个无关的变量,一个读,一个写,而这两个变量在一个cache line里面。那么写会导致cache line失效(通常是在多核编程里面,两个变量在不同的core上引用)。读写分离是一个很难运用的技巧,特别是在code很复杂的情况下。需要不断地调试,是个力气活(如果有工具帮助会好一点,比如cache miss时触发cpu的execption处理之类的)。

    18) Reduce duplicated code(减少冗余代码),推荐指数:5颗星

    代码里面的冗余代码和死代码(deadcode)很多。减少冗余代码就是减小浪费。但冗余代码有时又是必不可少(copy-paste太多,尾大不掉,不好改了),但是对critical path,花一些功夫还是必要的。

    19) Use compiler optimization options (使用编译器的优化选项),推荐指数:4颗星

      使用编译器选项来优化代码,这个应该从一开始就进行。写编译器的人更懂CPU,所以可以放心地使用。编译器优化有不同的目标,有优化空间的,有优化时间的,看需求使用。

    20) Know your code path (了解所有的执行路径,并优化关键路径),推荐指数:5颗星

      代码的执行路径和静态代码不同,它是一个动态的执行过程,不同的输入,走过的路径不同。我们应该能区分出主要路径和次要路径,关注和优化主要路径。要了解执行路径的执行流程,有多少个锁,多少个原子操作,有多少同步消息,有多少内存拷贝等等。这是性能优化里面必不可少,也是唯一正确的途径,优化的过程,也是学习,整理知识的过程,虽然有时很无聊,但有时也很有趣。

     

     

     

     

     

     

     

     

     

     

     

     

    何时应该优化

    如果数据表明,性能确实没有达到指标,特别是当profiler表明,某处关键路径上的代码执行占用了大量的时间,那么就是优化的时候了。

     

    首先,要确保你要优化的代码是正确的,没有任何已知bug。因为优化后的代码往往会变得更复杂而难以修改,所以要趁代码还比较简单的时候赶紧把bug都修掉吧。

      然后,要确认性能指标,可以查specification,或者如果不清楚的话再问问客户,或者根据其他功能性需求计算得出。用profiler收集目前的性能数据,和性能指标对比,以确定是否需要优化、哪里需要优化。(数据要保留,因为等优化完后还要用这些数据来做对比,以检查优化是否有效。)常见的profiler有Rational Quantify、Borland Optimizeit等等。很多UNIX下面都自带了profiler,比如prof、gprof等,对于一般的使用已经够了。

      第三,进行优化。后面“常用的优化方法”一节对此进行了详细介绍。可以照着列出的常见的优化方法一个个地套用,或者更好的办法是进行一次团队头脑风暴会议,让大家提出各种可能的优化方案。记得优化时不要删除原来的实现。可以在源文件中以替代函数或者注释的方式保留原来的实现。

      第四,使用profiler,验证优化是否如所想的那样有效。如果有效,那是最好;如果无效甚至是帮了倒忙,那么就赶紧取消改动,使用原来的版本,然后继续尝试其他的优化方案。记得优化要一步一步来,从最省事且最有效的方案到最麻烦且收益最小的方案。一旦达成性能指标就收手,不要恋战。

      最后,记得对优化过的代码执行单元测试,看看有没有为了性能牺牲了正确性。要记得在注释或者文档中为优化留下记录。

     

    常用的优化方法

      最简单的优化:请检查是否使用了编译器的最新版本,是否把优化编译开关打开了,是否正确指定了目标处理器(以便使用MMX、SSE、3DNow!等高性能指令集以及让编译器自动为处理器所支持的其他高级特性做优化)。如果发布的产品要支持多种处理器,那么如果可能的话,请单独为每种处理器进行编译,分别发布,或者使用同一个发布包但让安装程序自动检测处理器型号并安装对应的二进制版本,或者把会在关键路径上执行的代码封装成动态链接库,然后让程序启动时自动检测处理器型号并加载为相应型号优化过的动态链接库版本。

      还有,要确保使用了高性能的库,好的算法。比如,同样是从堆上分配内存,不同编译器提供的malloc或者new的实现,性能差异就不小。GCC使用的DL malloc就比较高效,Borland的编译器提供的实现使用了类似内存池的方式来动态管理内存,效率也很高,但也有些编译器对此并没有做什么优化,直接进行系统调用。不仅malloc/new如此,STL的allocator也是如此。SGI STL带的allocator为小于128字节的内存块的分配进行了特别优化(用内存池实现),所以小型字符串以及其他会用到allocator此项功能的操作都会性能比较好,但其他STL实现就没有做这样的优化。

      选择正确的算法,往往比优化地实现算法更重要。因为不同时间复杂度的算法可能会给性能带来几个数量级的差异,而实现上的优化则往往付出很大、所得甚少。如果有时候精度不是那么重要,或者不需要找最佳的结果只需要找近似最佳的结果,那么往往可以用低时间复杂度的近似算法来代替。

      另外,查表法也是个常用的技巧。假设,用某个公式可以把彩色图像转换成灰度图象,那么如果转换处理量很大的话,对每个象素都用该公式计算一边就不划算了,完全可以事先对所有颜色都计算好,然后处理时查表即可。对三角函数也是如此。当然,为了减小表的尺寸,在精度上往往需要牺牲一些。

      但也不要以为因为是预先计算的不需要考虑计算代价,或者内存比较大虚拟内存更大,就可以把表做得很大。记住操作系统或者操作系统进行内存换页或者Cache换页都是要时间的,两个临界点分别是Cache的尺寸和物理内存的尺寸。具体是全部计算,还是全部查表,还是部分计算部分查表,表要做得多大,这些都需要尝试并用实际数据来支持。一个比较复杂的做法是动态地把计算出来的值缓存到稀疏表中并供以后使用时查询,表的物理尺寸根据当时机器的Cache、内存状况动态配置。

      如果使用Java或者.NET上的编程语言的话,因为垃圾会占用空间,垃圾收集器的执行会占用时间,所以除了优化算法及其实现,还要注意你的代码对垃圾收集器是否友好。比如有没有及时把不用的引用置成null,有没有不必要的finalizer等等。

      要避免很大的循环体,因为它们往往会超出Cache的尺寸。尽可能避免复杂的if-else或者switch-case语句,因为现代CPU的乱序执行功能看见这些语句会觉得很无奈。即便你非要用这些语句,最好养成习惯,把最可能的分支放在最前面。还有,如果可能的话,不要在循环体中使用这些条件分支语句。

      有一些经典著作,如ThePractice of Programming(《程序设计实践》)、Programming Pearls(《编程珠玑》)、CodeComplete 2e(国内目前只出版了第1版,叫《代码大全》)也都提到了很多优化技术,但是,很重要的一点是,这些书都很少提到或者没有展开讲“构架设计时注意不要留下性能瓶颈或者缺陷”这个问题。这已超出了优化的范畴,而是要求在设计起始阶段时就考虑到性能需求。事实上,在硬件性能极大提高、优化编译器大行其道的今天,我们写程序时已基本上很少需要去考虑局部的微观实现是否优化了,因为有95%的可能编译器会替你去操心,或者根本性能不优化也可满足需求。甚至如果程序的内部结构比较清晰的化,算法也是可以很容易地替换的(比如用Strategy模式,或者Policy-Based Design的方式)。但也有的东西不太好在程序写完后再改,但又可能对性能有极大影响:那就是总体的设计和构架,以及一些影响面很广的设计决策/取舍。在今天,这些比较宏观的内容远比微观的优化技巧要重要。

      读者可能要问了:“不是说‘不必要的优化是一切罪恶的源泉’、‘没有数据证明就不要做优化’吗?在设计起始阶段根本还没有代码可以执行,怎么获得数据?你怎么保证这不会是不必要的优化呢?”噢,这个问题很好回答:当设计还没形成,代码还没写时,这不叫优化,仅仅是设计。优化是一种改变,把现有的缓慢的东西变成快速的东西。而设计时“本来无一物,何来谈优化”呢。

      更何况,一些比较宏观的构架上的决策,日后重构起来会非常困难,所以一开始就应该要考虑到。如果一开始需求尚未明确并且你也预计不到日后会有这样的性能需求,那么没有考虑到也不能怪你。但若一开始客户就提出了明确的性能要求,或者你心里很清楚客户一定会需要这样的性能,而你设计时却依然选择了无法或者难以满足这样性能要求的构架,那么这就不太好了。此外,如果两种设计/构架,并没有明确的实现复杂性或者优雅程度的差别,而其中一种设计/构架明显性能扩展性更好,那么也应该选择后一种。这不叫“premature optimization”,而叫做“避免premature pessimization”(过早悲观)(见C++ CodingStandards一书的Item 9)。

      另外,还有一些很常见的和性能相关的话题。而且不少人对它们的认识还有一些误区,比如资源(特别是内存)的获取和释放、线程间的同步(也可看作特殊资源——各种线程锁的获取和释放)、字符串(或者其他缓冲区)的处理,以及这些操作的组合。这些话题很值得进一步讨论,在今后的文章中,会再和读者进行更深层次的交流。

     

     

     

     

     

     

    分支优化

    局部性优化

    循环展开

     

    展开全文
  • 工作中性能优化总结

    千次阅读 2016-08-20 19:57:01
    工作中性能优化总结 性能优化

     一.分布式服务化,各个服务结点无状态,当一个机子有性能问题时,可通过加机子来解决

     二.单个服务的性能优化,可以从从jvm,业务代码,数据库,缓存来做优化。

        1.jvm优化

          高并发互联网应用,要求响应延迟小,这时垃圾回收可以采用cms+parNew。其次分配合适的年轻代,老年代。一般老年代是年轻代的2倍大小左右,比如2048M堆间,默认的年轻代大小一般为768M。这里如果perm区设置为100M,则老年代大小为1180M. 

        一般出现大量full gc,就要看是否堆大小,或者老年代太小。如果频繁出现担保失败,就要考虑调低老年代full gc比率(或者调高老年代大小),使用老年代大小*触发垃圾回收比率>年轻代大小。默认如果老年代空间使用了75%时就会触发full gc。这里频率出现担保失败会严重影响响应时间。因为担保失败后,垃圾回收会使用serial垃圾回收,这个会暂停应用程序,直到垃圾回收完成。

      2.业务代码优化

        这部分,主要看业务场景。工作中对于用户不太变的数据可以使用缓存。在做的项目里,用户相关的账户也是有缓存的。

        其次减少与数据库交互次数。如在获取一个活动下相关产品时(eg:有10个产品),这时不要循环这10个产品id,最好用 in一次性从数据库中取出这10个产品。

        计算用户收益,典型的公式=用户昨日持仓*利率,而昨日持仓如果做快照,则还需要查找这个快照多了一次数据库交互。这里可以将用户昨日持仓保存在账户上,如何实现?

    账户上保存两个字段:s0, s1。每天用户持仓只更新其中一个字段,另一个字段就是昨日持仓。eg,今日10日,当前更新持仓时只使用s0,则s1是昨日持仓。

       对于不是要求必须强一致的业务场景,而又要一定程度上保证一致性,可以使用redis锁。如工作中,对于用户每日取现次数限制可以通过redis锁来实现。

      减少锁粒度。比如业务有个取现业务,基本流程需要:校验账户状态是否正常,当前是否能够取现,减用户资产。这里在前面校验部分可以不加锁,等到减用户持仓时再加行级锁。

      更新数据库记录时要用索引,最好用主键更新。否则会导致全表锁。

      能够异步做的事情异步做,不要在核心流程。比如传统电商下单流程,需要做生成订单,扣用户钱,修改订单状态为:下单成功,生成物流出库单。这里生成物流出库单可以异步生成。

      大表间不要做join。性能会很差。业务如果是非实时数据,需要展示A表和B表的关联的内容。可以定时生成A表和B表关联的数据供用户查。

      物流项目中,每个网点都有自己的账户,而它们都挂在百世账户下。当网点经过分拨中心时,需要收取网点的手续费,业务上就是网点减去X元,百世账户加上X元。如果这里账户更新操作都是实时的话,可以想像如果有1万个网点做并发时,性能几乎是不能满足要求的。这里可以采用先记下费用发生记录。每隔5分钟再去捞数据进行入账,这个一般单线程去做。

      秀品项目中,由于是一个闪购商城,热门商品的库存会成为性能瓶颈。这里可以将热门商品库存放入redis主从集群。之后增减库存走redis。当然这有个风险如果主从缓存同时挂了并且不能恢复,之后只有从数据库恢复库存。这时会发生超卖现象。解决方案可以是减库存成功后,发一个异步消息到mq中,记录下库存增减。之后如果缓存都挂了可以从记录的库存事件恢复。

      首页性能优化,由于系统刚上线,且当时对于CDN,如果首页发生变化cdn变化会发生延迟。这时采用的方案是将首页数据加载到缓存中。有两块缓存,早上10点前访问缓存块1,10点后访问缓存块2.每天凌晨12点会将缓存块1替换成昨日10点后的内容,然后预热今日10点后新的首页内容到缓存块2.

      秀品购物车优化,库存增减在redis主从集群。有个需求是当用户放入购物车的商品超过20分钟后,需要释放库存。由于在redis中会记录下每个用户对应的商户购买数量,如果每1分钟或5分钟扫描redis全部用户,那当用户量大时,一方面造成了不必要数据扫描,比如10点用户刚放入商品到库存,理论这个用户商品库存过期时间要等到10点20分。而这时任务在10点03分启动了,这时就要扫描这个用户,造成了不必要的扫描。优化方案是利用一个循环队列,共5个数组位置。每个位置可以存在用户及对应最后更新时间。有一个指针指向下次job要清理的区域。如当前指针指向1区域,如果用户在此时发生增减商品操作则将当前用户及对应更新时间放入0区域。job任务5分钟处理一次,这样job需要处理完1,2,3,4才能最后处理0位置。这里1和0位置需要忽略,因为有可能用户及相关更新时间刚放入0位置,清理job就已经启动处理1位置了。共需要20分钟。

      购物车有个业务场景是,用户查看自己的购物车。这时需要展示用户购买商品的所属档期信息,商品信息,相应可用优惠券等信息。用户购买的sku及对应数量放在redis中。这里先根据sku集合获取到商品集合,然后合计出对应档期,查询档期部分用dubbo的异步调用去获取(会返回一个future)。然后调用优惠券服务,最终档期异步future获取到档期信息,最终组合成结果返回给前端。


    3.数据库优化

       涉及到分表分库。读写分离。

       读写分离,一般用mysql的主从。主负责写,从库可以提供读。这样可以提供读性能。

       当业务的写成为瓶颈时,就要分库了。这时可以按业务垂直拆分。业务中,商品,交易,促销,用户可以是独立库。

       分表是因为当一个表比较大时,读性能会下降。

       分库分表后,会涉及数据查询问题。

       sql级别一般要走索引。避免全表扫描。不要在查询条件中用sql函数,会导致不走索引。

       避免大表join,避免like。

    4.缓存方面

       本地缓存,如果有些字典类几乎不变的数据配置可以在jvm中(数据量不大的情况下),减少从redis获取(网络开销)。

      

       

       

    展开全文
  • ABAP程序优化的一些总结

    千次阅读 2015-02-04 22:53:58
    最近做了一个数据迁移的程序,了解学习了不少优化程序的方法,下面总结下。   第一部分 程序运行时间的优化 优化的重点之一是运行时间,影响运行时间的因素有很多,首先程序本身的话,从CPU角度讲,指令越少自然...

    【博主ABAP学习交流群53144545

     

    第一部分 程序运行时间的优化

    优化的重点之一是运行时间,影响运行时间的因素有很多,首先程序本身的话,从CPU角度讲,指令越少自然运行时间越快,会引发CPU大量操作的基本上主要就是对数据量非常大的内表进行循环处理(LOOP)的时候,下面列举一些比较有效的可以优化的地方:

     

    嵌套循环(NESTEDLOOP),如果外循环有N条记录,内循环有M条记录,那么总的循环次数就是N*M,这是需要避免或者优化的,一个比较常用的方法是在LOOP后面加上WHERE条件,这样可以避免了全表循环,在一些特殊场合(比如排序过的内表),可以使用一些编程技巧用INDEX FROM TO直接指定需要LOOP的内表所在行。

     

    二分法查找(BINARYSEARCH):假如LOOP里有需要用到READ TABLE去查询另外一个内表里的某条记录,那么先对那个内表进行需要比较的KEY值的排序,再用BINARY SEARCH能显著提高查询速度。如果不指定BINARY SEARCH关键字,系统会按顺序作全表扫描和比较,扫描一个有N条记录的内表,在最坏情况下需要比较N次(假如所需要的记录正好在内表的最后一条),而用BINARYSEARCH查找是折半查找,则只需要比较LOGN次。比如当N是100万时,普通查找最多可能查找100万次,而折半查找最多只需要查找20次。

     

    FIELD-SYMBOL:LOOP时使用FS来代替结构(工作区),这样的好处是直接定位到内表的某条记录,而不是复制这条记录到结构里(所以在修改内表内容时,使用指针不需要MODIFY命令,因为指针是直接修改内表的内容,而结构只是内表当前行的一个拷贝,修改完结构之后需要通过MODIFY复制回内表)。同样的READTABLE也一样可以ASSIGNING到一个指针里,但是和LOOP不同的是,LOOP ASSIGNING是必定成功的,而READ TABLE ASSINGING可能失败,当失败时,这个指针是不能使用的,不然运行时会发生指针未分配的DUMP。建议可以定义一个同类型的结构(初始值),如果READ TABLEASSIGNING失败,就把指针分配到这个结构里。

    *使用指针来进行LOOP基本上总是有利的且没有什么副作用,但是个人认为从原理上看,有的时候可能优化的重点并不是LOOP时用指针去代替结构,假如LOOP里的数据处理才是重点的话,那么省去复制到结构的这步操作也不会对运行时间产生很大的影响。

     

    数据库操作:这点其实属于数据库开销的方面的优化。LOOP里基本不允许SELECT,一次完整的SELECT所需要的时间相对是较多的(可能只有零点零几秒,但是一旦循环几十万上百万次的话……),一般是先用FOR ALL ENTIRES把全部数据读入另外一个内表,然后和LOOP里面READ TABLE(BINARY SEARCH)来代替。同理在作INSERT和UPDATE的时候,也尽量通过内表批量处理,避免在LOOP里根据结构作单条插入或更新。

    *这样做虽然提升了时间上的效率,但内存和数据库传输数据开销是增加了,因为需要从数据库读入更多的数据到内表,可能导致内存不够的问题,具体会在后面的内存空间相关的部分讲。

     

    调用功能模块(Function Module)和方法(Method):有的时候会在LOOP里调用标准的FM,标准的FM通常比较复杂,内部有多层次的调用,里面可能会有SELECT语句,这种情况下可以自己写逻辑从数据库取数来代替调用标准FM。

     

    并行处理(Parallel Processing):并行处理是指开多个进程同时处理,SAP有多种进程,前台进程(DialogProcess)和后台进程(BackgroundProcess)是其中的两种,并行可以分为前台并行(占用前台进程)和后台并行(后台进程),前者采用异步RFC,关键字CALLFUNCTION STARTING NEW TASK,后者采用JOB OPEN和SUBMIT VIA JOB的方法。并行处理是在处理大量数据时非常有用的方式,除了可以显著地提升时间效率之外,也可以解决单个进程有最大内存的限制。但是并行也有局限性,比如说同时处理的数据不能有逻辑上的关联性,而且每个进程的执行先后顺序也是不可预料的。

     

    第二部分 内存空间的优化

    SAP Unicode程序,一个C类型的字符占用两个字节(2Byte)。内存设置方面,分配给一个前台进程(Dialog Process)默认是4000000000字节(2000000000的Extended Memory和2000000000的Heap Memory,这个大小可以调整),也就是不到4GB的内存空间,当一个内表的行结构是512个字符(1024个字节时),最多大约可以容纳400万条不到的记录数,如果超过了这个容量,就会发生DUMP或者程序强制关闭。

    解决方法是SELECT取数时改为使用SELECT PACKAGE SIZE ENDSELECT分包取出并处理,这样可以在规定的内存大小内完成数据的处理。但是这种方法依然有个小问题,就是在SELECT和ENDSELECT里不支持并行进程(从原理上讲并行会涉及到进程切换,进程切换时会触发一个COMMITWORK,而在SELECTENDSELECT里这是不允许的),如果要达到分包取数并且并行处理的效果,就需要使用OPEN CURSOR和FETCH。

     

    第三部分 数据库操作优化

    展开全文
  • 关于Xcode编译性能优化的研究工作总结本文为原创文章,转载注明出处,谢谢!本文链接:关于Xcode编译性能优化的研究工作总结近来(8月1–8月12)结合Xcode的官方文档和网上资料经验对Xcode的一些配置选项进行了编译...
  • 程序性能优化总结

    2019-01-09 12:26:06
    初始化小程序环境是微信环境做的工作,我们只需要控制代码包大小,和通过一些相关的缓存策略控制,和资源控制,逻辑控制,分包加载控制来进行启动加载优化。 勾选开发者工具中, 上传时压缩代码(若采用w...
  • WPF程序性能优化总结

    千次阅读 2017-08-25 13:11:11
    WPF程序性能由很多因素造成,以下是简单地总结: 元素: 1、 减少需要显示的元素数量:去除不需要或者冗余的XAML元素代码. 通过移出不必要的元素,合并layout panels,简化templates来减少可视化树的层次。这可以...
  • vivado HLS 优化总结以及相关流程

    千次阅读 2020-05-27 18:27:38
    - - 数组分割七、函数层面优化InlineAllocationDataflow八、总结分析改善吞吐率( Throughput )改善时延( Latency)改善资源( Area) 一、for 循环优化之pipeline 二、for 循环优化之unroll 在默认情况下 for ...
  • //aga8.h:进行一些函数的声明 #ifndef __AGA8_H #define __AGA8_H #include "sys.h" #include <math.h> #define NUM 21 //定义当前气体组分数 #define R 8.31451e-3f //定义气体常数 double Calculate_Z...
  • 程序性能优化总结

    千次阅读 2012-10-12 10:25:41
    程序性能优化最显著的优化方法是算法上的优化,算法的优化对性能的提升往往是一个数量级的,例如排序,冒泡的时间复杂度为O(N^2),而快速排序的时间复杂度为O(Nlog(N)),这种性能的提供是非常明显的。 2. 消除...
  • 回想起自己从事Android移动终端的开发工作也有一段时间了,总结一下工作流程,梳理从产品需求的提出到版本发布的过程的各个步骤以及注意事项。
  • [前端优化]webAPP优化总结

    万次阅读 2017-01-05 14:21:26
    之前一直做算法,随后在公司做平台软件及后台,一个产品开发好-上线,优化一直伴随着整个过程,之前在公司做的一款平台软件,从上线到最终的稳定几乎花了一年时间,其中大部分都在做系统的优化. 优化的前提是软件的...
  • cache性能优化总结

    万次阅读 2018-01-12 19:00:43
    cache性能优化总结
  • 性能优化总结

    千次阅读 2011-12-01 17:42:41
    关于性能优化方面的关注点,每天都能够朗朗上口,但是当具体拿出一个页面给你去...当我想到了这一点,就开始着手去总结这个一个优化流程的东西。 当然了,做优化的脱离不了浏览器,也脱离不了工具,为此,你手上得准
  • 日常程序性能优化过程中的一些总结,记录于此以备忘
  • CUDA优化总结

    千次阅读 2016-12-16 17:55:30
    总体的CUDA优化分以下几个策略: 最大化并行执行来获得最大的利用率 优化内存使用方法来获得最大的内存吞吐量 优化指令使用方式来获得最大的指令吞吐量 具体再可以从以下几个方面入手:CUDA配置优化:指的是在CUDA核...
  • Android 包大小优化总结

    万次阅读 2021-01-23 21:49:33
    Android 包大小优化总结 众所周知,尽量减少 APP 安装包的大小是非常必要的,减少安装包大小,不仅减少了用户下载时的网络流量消耗,而且还减少了用户的下载等待时间。更重要的是安装包大小也会影响下载转化率。 ...
  • OpenCV常见的优化方法和技巧总结

    万次阅读 多人点赞 2018-02-24 15:53:36
    OpenCV常见的优化方法和技巧总结 【尊重原创,转载请注明出处】http://blog.csdn.net/guyuealian/article/details/78540206 目录 OpenCV常见的优化方法和技巧总结 一、OpenCV常见的优化方法总结 1.1 cv::...
  • 智能优化算法总结

    万次阅读 多人点赞 2015-12-15 21:56:55
    优化算法有很多,经典算法包括:有线性规划,动态规划等;改进型局部搜索算法包括爬山法,最速下降法等,模拟退火、遗传算法以及禁忌搜索称作指导性搜索法。而神经网络,混沌搜索则属于系统动态演化方法。梯度为基础...
  • 秒杀性能优化实践总结

    千次阅读 2016-04-18 20:02:20
    秒杀优化实践总结
  • Spark优化总结

    千次阅读 2016-08-06 10:54:05
    本篇文章是关于我在学习Spark过程中遇到的一些问题及总结,分为Spark优化、RDD join问题、遇到的问题、总结、参考资料几个部分。 一:Spark优化 1、设置序列化器为KryoSerializer  Spark默认使用的是Java序列化...
  • 程序优化总结--万变不离其宗

    千次阅读 2012-12-12 16:56:14
    程序优化总结--万变不离其宗 空间优化, 时间优化. 目前我们就在PC上,所以主要目标是时间优化. 第一步: 算法结构层面的优化.  搞懂算法,从宏观层面优化结构,能在一个层面做的事情,尽量合并一起做! ...  第二...
  • Android性能优化总结

    万次阅读 多人点赞 2019-06-18 21:45:50
    安卓开发大军浩浩荡荡,经过近十年的发展,Android技术优化日异月新,如今Android 9.0 已经发布,Android系统性能也已经非常流畅,可以在体验上完全媲美iOS。 但是,到了各大厂商手里,改源码、自定义系统,使得...
  •  -pm:在程序级别进行优化。可以将所以文件联合在一起进行优化,主要有去掉没有被调用的函数、总是常数的变量 以及没有使用的函数返回值。  -ms0:不使用冗余循环进行优化,减小程序的大小。一般情况下这个选项对...
  • Android APP性能优化(最新总结)

    千次阅读 2018-01-13 11:35:54
    安卓大军浩浩荡荡,发展已近十个年头,技术优化日异月新,如今Android 8.0 Oreo 都发布了,Android系统性能已经非常流畅了。但是,到了各大厂商手里,改源码自定系统,使得Android原生系统变得鱼龙混杂,然后到了...
  • hive优化总结

    万次阅读 多人点赞 2014-10-31 09:21:58
    优化时,把hive sql当做map reduce程序来读,会有意想不到的惊喜。 理解hadoop的核心能力,是hive优化的根本。这是这一年来,项目组所有成员宝贵的经验总结。   长期观察hadoop处理数据的过程,有几个显著...
  • Spark性能优化总结

    万次阅读 2016-11-13 21:51:31
    近期优化了一个spark流量统计的程序,此程序跑5分钟小数据量日志不到5分钟,但相同的程序跑一天大数据量日志各种失败。经优化,使用160 vcores + 480G memory,一天的日志可在2.5小时内跑完,下面对一些优化的思路...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 429,015
精华内容 171,606
关键字:

优化工作流程总结