一、基础概念

Oracle 11g开始,提供了一种新的固定执行计划的方法,即SQL plan baseline,中文名SQL执行计划基线(简称基线),可以认为是OUTLINE(大纲)或者SQL PROFILE的改进版本,基本上它的主要作用可以归纳为如下两个:

1、稳定给定SQL语句的执行计划,防止执行环境或对象统计信息等等因子的改变对SQL语句的执行计划产生影响!

2、减少数据库中出现SQL语句性能退化的概率,理论上不允许一条语句切换到一个比已经执行过的执行计划慢很多的新的执行计划上!

注意:

1、从Oracle的发展角度来看,估计这种方法是Oracle发展和改进的方向,如今outline已经被废弃,sql profile估计在后续的发行版本中也难有改进,因此,对于从11g开始接触Oracle的朋友来说,一定要对sql计划基线有所了解,因为这是以后的主流!

2、SQL执行计划基线保存在数据字典中,查询优化器会自动判断使用他们。

二、工作机制

从Oracle 11g开始,由于基线的存在,一条语句的解析过程大概如下:

SQL语句被硬解析后,CBO(优化器)会产生很多个的执行计划,CBO从中选择一个成本最低执行计划。

基于SQL语句的文本形成一个哈希值(signature),通过这个哈希值来检查数据字典中是否存在同样的基线。

如果基线存在,优化器会对刚刚产生的执行计划和保存在SQL plan baseline中的执行计划进行比较。

如果基线中有与CBO刚产生的执行计划的匹配的SQL执行计划存在,并且被标记为可接受(‘accepted’),则这个CBO生成的执行计划被启用。

如果基线中没有匹配的SQ执行计划存在,CBO评估基线中被标记为‘accepted’的的多个执行计划,并选择其中cost最低的执行计划。(注意,一个语句的基线可以有多个执行计划被保存,这是与其他Outline和SQL profiel都不同的地方)

如果刚刚硬解析过程中CBO选择的执行计划比保存在基线中的执行计划COST都低,这个新生成的执行计划被标记为‘not-accepted’并保存在基线中。直到这个执行计划被演化且验证后才会被考虑使用,即标记为accepted(演化和验证,可以简单理解为Oracle确认这个执行计划可以带来更好的性能)。

Oracle 就是通过上面这种方式来确保SQL语句的性能不会退化(即第一部分中我归纳的第二个主要作用),称为“执行计划保守选择策略”

三、基线的一些特点

简单归纳如下几个

通过OPTIMIZER_USE_SQL_PLAN_BASELINE来控制Oracle是否使用基线,默认值为TRUE,即会自动使用基线。

11g中默认是不会自动创建基线

与OUTLINE和SQL Profile不同,基线中不存在分类的概念

与OUTLINE和SQL Profile不同,每个SQL语句可以有多个基线。Oracle根据制定的规则来判断具体是否哪个基线

基线针对RAC中所有的实例都生效

基线有两个表示,一个为sql_handle,可以理解为表示语句文本的唯一标识,一个为sql_plan_name可以理解为执行计划的唯一标识

不能像sql profile一样通过force_matching属性将字面值不一样的SQL语句使用一个基线应用多个语句。

三、创建基线的几种方式

1、自动捕获基线,通过将optimizer_cature_sql_plan_baselines设置为true,优化器为重复执行两次以上的SQL语句生成并保存基线(可以系统级或会话级修改)

2、从SQL调优集合中加载,通过使用包dbms_spm.load_plans_from_sqlset来从SQL调优集合中加载基线

DECLARE

l_plans_loaded PLS_INTEGER;

BEGIN

l_plans_loaded := DBMS_SPM.load_plans_from_sqlset(

sqlset_name => 'my_sqlset');

END;

/

3、从库缓存中加载,通过包dbms_spm.load_plans_from_cursor_cache函数为一条已经在游标缓存中的语句创建基线

DECLARE

l_plans_loaded PLS_INTEGER;

BEGIN

l_plans_loaded := DBMS_SPM.load_plans_from_cursor_cache(sql_id => '1fkh93md0802n',plan_hash_value=>null);

END;

/

备注:可以有多种方式加载,例如可以根据sql文本进行模糊匹配、SQL语句解析的用户名等等方式,具体见文档

四、基线的几种状态

一个SQL语句对应的基线,我将它们归纳为三种状态

accepted(可接受),只有这种状态的基线,优化器才会考虑此基线中的执行计划

no-accepted(不可接受),这种状态的基线,优化器在SQL语句解析期间不会考虑。这种状态的基线必须通过演化和验证通过后,转变为accepted状态后,才会被优化器考虑使用

fixed为yes(固定),这种状态的基线固有最高优先级!比其他两类基线都要优先考虑

五、查看基线

1、基本视图:dba_sql_plan_baselines、dba_sql_management_config

2、底层视图:sqlobj$data 、 sqlobj$ (保存具体的hint),如下查看基线中保存的执行计划语句:

select

extractvalue(value(d), '/hint') as outline_hints

from

xmltable('/outline_data/hint'

passing (

select

xmltype(comp_data) as xmlval

from

sqlobj$data sod, sqlobj$ so

where so.signature = sod.signature

and so.plan_id = sod.plan_id

and comp_data is not null

and name like '&baseline_plan_name'

)

) d;

3、通过函数来查看基线的详细信息:

select * from table(dbms_xplan.display_sql_plan_baseline(sql_handle=>'SYS_SQL_11bcd50cd51504e9',plan_name=>'SQL_PLAN_13g6p1maja1790cce5f0e'));

六、演化基线

为了验证基线中一个处于不可接受状态的执行计划是否比一个处于可接受状态的执行计划具有更高的效率,必须通过演化来验证,需要让优化器以不同的执行计划来执行这条SQL语句,观察不可接受状态的执行计划基线是否会带来更好的性能,如果性能确实更高,这个不可接受状态的基线将会转换为可接受状态。演化的方式有两种:

1、手工执行运行

SELECT DBMS_SPM.evolve_sql_plan_baseline(sql_handle => 'SYS_SQL_xxxxxxxxxxxxx') From dual;

还有time_limit/verify/commit几个参数,可以参考文档

2、调优包实现基线的自动演化,可以理解为,启动一个调度任务,周期性的检查是否有不可接受状态的基线可以被演化

七、修改基线

可以通过dbms_spm.alter_sql_plan_baseline包来修改基线的一些属性,主要有如下几个属性

ENABLED :设置该属性的值为NO告诉Oracle 11g临时禁用某个计划,一个SQL计划必须同时标记为ENABLED和ACCEPTED,否则CBO将忽略它

FIXED:设置为YES,那个计划将是优化器唯一的选择[最高优先级],即使如果某个计划可能拥有更低的成本。这让DBA可以撤销SMB的默认行为,对于转换一个存储概要进入一稳定的SQL计划基线特别有用,注意当一个新计划被添加到被标记为FIXED的SQL计划基线,该新计划不能被利用除非它申明为FIXED状态

AUTOPURG:设置这个属性的值为NO告诉Oracle 11g无限期保留它,从而不用担心SMB的自动清除机制

plan_name : 改变SQL plan 名字

description : 改变SQL plan描述

语法:

SET SERVEROUTPUT ON

DECLARE

v_text PLS_INTEGER;

BEGIN

v_text := DBMS_SPM.alter_sql_plan_baseline(sql_handle => 'SYS_SQL_xxxxxx',plan_name => 'SYS_SQL_PLAN_xxxxxxxxx',

attribute_name => 'fixed',attribute_value => 'YES');

DBMS_OUTPUT.put_line('Plans Altered: ' || v_text );

END;

/

八、迁移基线

dbms_spm提供了多个过程来在数据库之间迁移SQL计划基线

create_stgtab_baseline创建一个计划基线保存表

pack_stgtab_baseline将基线从数据字典复制到第一步的表中

unpack_stgtab_baseline将基线从保存表中复制到迁移数据库的数据字典中

大概过程如下:

1、创建一张保存数据字典中基线表内容的用户表

exec dbms_spm.create_stgtab_baseline(table_name => 'BASELINE_TEST',table_owner => 'SCOTT',tablespace_name =>'');

2、将数据字典中基线表的内容 插入到 第一步创建的用户表中

exec :i := dbms_spm.pack_stgtab_baseline(table_name => 'BASELINE_TEST', table_owner => 'SCOTT');

备注:可以支持多种方式插入,例如包含特定字符的SQL相关的基线,sql_handle来精确识别一个基线,具体见文档

3、通过迁移工具迁移用户表

exp/imp or expdp/impdp

4、将迁移过来的用户表中保存的基线内容 插入到当前库的数据字典中,从而实现迁移

exec :i := dbms_spm.unpack_stgtab_baseline(table_name => 'BASELINE_TEST',table_owner => 'SCOTT');

备注:可以支持多种方式,与步骤2一样,具体见文档

九、删除基线

可以通过dbms_SPM.drop_sql_plan_baseline包来手工删除数据字典里的基线

为使用的基线,fixed为no的基线,将在一定的保留期后自动删除(可查看dba_sql_management_config视图)

手工删除方法如下

SET SERVEROUTPUT ON

DECLARE

v_text PLS_INTEGER;

BEGIN

v_text := DBMS_SPM.drop_sql_plan_baseline(sql_handle => 'SYS_SQL_7b76323ad90440b9',plan_name => NULL);

DBMS_OUTPUT.put_line(v_text);

END;

/

十、将一个SQL语句固定为我们期望的执行计划

我一般通过如下几步实现(仅供参考)

1、为这个SQL语句创建基线

2、给这个SQL语句添加hint赖宇星,确保SQL语句添加hint后的执行计划与我们期望一样

3、将第2步产生的执行计划,添加到第一步创建的基线中(注意,前面已经说过,一个SQL语句可以有多个基线!)

4、删除基线中第1步创建的那个执行计划(这样,我们就可以确保基线中只有我们期望的执行计划,即保存第2步SQL语句的执行计划)

5、验证是否生效

后续有示例,加深理解!

十一、示例(将一个SQL语句固定为我们期望的执行计划)

首先运行两个结构相同的语句,下面的实验通过SQL计划基线,将一个语句的执行计划通过另一个语句的执行计划来固定

SQL> select sql_handle,plan_name,dbms_lob.substr(sql_text,60,1) sql_text,ACCEPTED from dba_sql_plan_baselines;

未选定行

SQL> alter system flush shared_pool;

系统已更改。

SQL> select /outlinetest2/ /+ full(dh_stat)/ * from dh_stat where id=711;

ID NAME TYPE

711 I_STREAMS_PROCESS_PARAMS1 INDEX

SQL> select /outlinetest3/ /+ index(dh_stat)/ * from dh_stat where id=711;

ID NAME TYPE

711 I_STREAMS_PROCESS_PARAMS1 INDEX

SQL> select sql_text,sql_id,hash_value,child_number,plan_hash_value,to_char(LAST_ACTIVE_TIME,'hh24:mi:ss') time

2 from v$sql a where sql_text like '%outlinetest%' and sql_text not like '%v$sql%';

SQL_TEXT SQL_ID HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE TIME

select /outlinetest2/ /+ full(dh_stat)/ from d 4vaj9fgjysy9c 3823925548 0 1845196118 12:27:31

h_stat where id=711

select / outlinetest3 / /+ index(dh_stat) / from fm35jcmypb3qu 4250242778 0 2780970545 12:27:41

dh_stat where id=711

SQL> select * from table(dbms_xplan.display_cursor('4vaj9fgjysy9c','',''));

PLAN_TABLE_OUTPUT

SQL_ID 4vaj9fgjysy9c, child number 0

select /outlinetest2/ /+ full(dh_stat)/ * from dh_stat where

id=711

Plan hash value: 1845196118

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

| 0 | SELECT STATEMENT | | | | 124 (100)| |

|* 1 | TABLE ACCESS FULL| DH_STAT | 1 | 38 | 124 (1)| 00:00:02 |

Predicate Information (identified by operation id):

1 - filter("ID"=711)

已选择19行。

SQL> select * from table(dbms_xplan.display_cursor('fm35jcmypb3qu','',''));

PLAN_TABLE_OUTPUT

SQL_ID fm35jcmypb3qu, child number 0

select /outlinetest3/ /+ index(dh_stat)/ * from dh_stat where

id=711

Plan hash value: 2780970545

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

| 0 | SELECT STATEMENT | | | | 2 (100)| |

| 1 | TABLE ACCESS BY INDEX ROWID| DH_STAT | 1 | 38 | 2 (0)| 00:00:01 |

|* 2 | INDEX RANGE SCAN | IND_1 | 1 | | 1 (0)| 00:00:01 |

Predicate Information (identified by operation id):

2 - access("ID"=711)

已选择20行。

SQL> DECLARE

2 k1 pls_integer;

3 begin

4 k1 := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE (

5 sql_id=>'4vaj9fgjysy9c',

6 plan_hash_value=>1845196118

7 );

8 end;

9 /

PL/SQL 过程已成功完成。

SQL> select sql_handle,plan_name,dbms_lob.substr(sql_text,60,1) sql_text,ACCEPTED from dba_sql_plan_baselines;

SQL_HANDLE PLAN_NAME SQL_TEXT ACC

SYS_SQL_11bcd50cd51504e9 SQL_PLAN_13g6p1maja17934f41c8d select /outlinetest2/ /+ full(dh_stat)/ * from d YES

h_sta

刚生产sql plan baseline的时候,第一次查询,无法找到执行计划,直到第二次执行的时候,才能看到,如下

SQL> select /outlinetest2/ /+ full(dh_stat)/ * from dh_stat where id=711;

ID NAME TYPE

711 I_STREAMS_PROCESS_PARAMS1 INDEX

SQL> select * from table(dbms_xplan.display_cursor('4vaj9fgjysy9c','',''));

PLAN_TABLE_OUTPUT

SQL_ID: 4vaj9fgjysy9c cannot be found

SQL> select sql_text,sql_id,hash_value,child_number,plan_hash_value,to_char(LAST_ACTIVE_TIME,'hh24:mi:ss') time

2 from v$sql a where sql_text like '%outlinetest%' and sql_text not like '%v$sql%';

SQL_TEXT SQL_ID HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE TIME

select /outlinetest3/ /+ index(dh_stat)/ * from fm35jcmypb3qu 4250242778 0 2780970545 12:27:41

dh_stat where id=711

SQL> select /outlinetest2/ /+ full(dh_stat)/ * from dh_stat where id=711;

ID NAME TYPE

711 I_STREAMS_PROCESS_PARAMS1 INDEX

SQL> select sql_text,sql_id,hash_value,child_number,plan_hash_value,to_char(LAST_ACTIVE_TIME,'hh24:mi:ss') time

2 from v$sql a where sql_text like '%outlinetest%' and sql_text not like '%v$sql%';

SQL_TEXT SQL_ID HASH_VALUE CHILD_NUMBER PLAN_HASH_VALUE TIME

select /outlinetest2/ /+ full(dh_stat)/ from d 4vaj9fgjysy9c 3823925548 0 1845196118 12:30:54

h_stat where id=711

select / outlinetest3 / /+ index(dh_stat) / from fm35jcmypb3qu 4250242778 0 2780970545 12:27:41

dh_stat where id=711

SQL> select * from table(dbms_xplan.display_cursor('4vaj9fgjysy9c','',''));

PLAN_TABLE_OUTPUT

SQL_ID 4vaj9fgjysy9c, child number 0

select /outlinetest2/ /+ full(dh_stat)/ * from dh_stat where

id=711

Plan hash value: 1845196118

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

| 0 | SELECT STATEMENT | | | | 124 (100)| |

|* 1 | TABLE ACCESS FULL| DH_STAT | 1 | 38 | 124 (1)| 00:00:02 |

Predicate Information (identified by operation id):

1 - filter("ID"=711)

Note

SQL plan baseline SQL_PLAN_13g6p1maja17934f41c8d used for this statement

已选择23行。

将符合我们预期的执行计划的加载到第一次生成的sql baseline中!

SQL> DECLARE

2 k1 pls_integer;

3 begin

4 k1 := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE (

5 sql_id=>'fm35jcmypb3qu',

6 plan_hash_value=>2780970545,sql_handle=>'SYS_SQL_11bcd50cd51504e9'

7 );

8 end;

9 /

PL/SQL 过程已成功完成。

可以看到,SYS_SQL_11bcd50cd51504e9下目前有两个plan_name

SQL> select sql_handle,plan_name,dbms_lob.substr(sql_text,60,1) sql_text,ACCEPTED from dba_sql_plan_baselines;

SQL_HANDLE PLAN_NAME SQL_TEXT ACC

SYS_SQL_11bcd50cd51504e9 SQL_PLAN_13g6p1maja1790cce5f0e select /outlinetest2/ /+ full(dh_stat)/ * from d YES

h_sta

SYS_SQL_11bcd50cd51504e9 SQL_PLAN_13g6p1maja17934f41c8d select /outlinetest2/ /+ full(dh_stat)/ * from d YES

h_sta

删除第一个plan_name,即将我们不需要的执行计划版本去除掉!

SQL> DECLARE

2 k1 pls_integer;

3 begin

4 k1 := DBMS_SPM.drop_sql_plan_baseline ( sql_handle=>'SYS_SQL_11bcd50cd51504e9',plan_name=>'SQL_PLAN_13g6p1maja17934f41c8d');

5 end;

6 /

PL/SQL 过程已成功完成。

通过下面的一部分测试,我们可以看到,新的SQL计划基线已经正常生效,及时语句中包含full提示,执行计划也走索引定位数据

SQL> select /outlinetest2/ /+ full(dh_stat)/ * from dh_stat where id=711;

ID NAME TYPE

711 I_STREAMS_PROCESS_PARAMS1 INDEX

SQL> select * from table(dbms_xplan.display_cursor('4vaj9fgjysy9c','',''));

PLAN_TABLE_OUTPUT

SQL_ID 4vaj9fgjysy9c, child number 1

select /outlinetest2/ /+ full(dh_stat)/ * from dh_stat where

id=711

Plan hash value: 2780970545

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

| 0 | SELECT STATEMENT | | | | 2 (100)| |

| 1 | TABLE ACCESS BY INDEX ROWID| DH_STAT | 1 | 38 | 2 (0)| 00:00:01 |

|* 2 | INDEX RANGE SCAN | IND_1 | 1 | | 1 (0)| 00:00:01 |

Predicate Information (identified by operation id):

2 - access("ID"=711)

Note

SQL plan baseline SQL_PLAN_13g6p1maja1790cce5f0e used for this statement

已选择24行。

可以通过dba_sql_plan_baselines来显示可用的SQL计划基线的一般信息,也可以通过如下这种方式显示执行SQL计划基线的详细信息!

select * from table(dbms_xplan.display_sql_plan_baseline(sql_handle=>'SYS_SQL_11bcd50cd51504e9',plan_name=>'SQL_PLAN_13g6p1maja1790cce5f0e'));

PLAN_TABLE_OUTPUT

SQL handle: SYS_SQL_11bcd50cd51504e9

SQL text: select /outlinetest2/ /+ full(dh_stat)/ * from dh_stat where

id=711

Plan name: SQL_PLAN_13g6p1maja1790cce5f0e Plan id: 214851342

Enabled: YES Fixed: NO Accepted: YES Origin: MANUAL-LOAD

Plan hash value: 2780970545

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

| 0 | SELECT STATEMENT | | 1 | 38 | 2 (0)| 00:00:01 |

| 1 | TABLE ACCESS BY INDEX ROWID| DH_STAT | 1 | 38 | 2 (0)| 00:00:01 |

|* 2 | INDEX RANGE SCAN | IND_1 | 1 | | 1 (0)| 00:00:01 |

Predicate Information (identified by operation id):

2 - access("ID"=711)

已选择26行。

查看SQL计划基线中保存的hint提示集合

SQL> conn /as sysdba

已连接。

SQL> select

2 extractvalue(value(d), '/hint') as outline_hints

3 from

4 xmltable('/outline_data/hint'

5 passing (

6 select

7 xmltype(comp_data) as xmlval

8 from

9 sqlobj$data sod, sqlobj$ so

10 where so.signature = sod.signature

11 and so.plan_id = sod.plan_id

12 and comp_data is not null

13 and name like '&baseline_plan_name'

14 )

15 ) d;

输入 baseline_plan_name 的值: SQL_PLAN_13g6p1maja1790cce5f0e

原值 13: and name like '&baseline_plan_name'

新值 13: and name like 'SQL_PLAN_13g6p1maja1790cce5f0e'

OUTLINE_HINTS

已选择6行。

oracle cpu基线,Oracle SQL执行计划基线总结(SQL Plan Baseline)相关推荐

  1. oracle 重设基线,Oracle SQL执行计划基线总结(SQL Plan Baseline)

    一.基础概念 Oracle 11g开始,提供了一种新的固定执行计划的方法,即SQL plan baseline,中文名SQL执行计划基线(简称基线),可以认为是OUTLINE(大纲)或者SQL PRO ...

  2. stored outlines迁移成SQL执行计划基线

    stored outline迁移 stored outline是对SQL语句的一组hint.hint指示优化器对SQL语句选择一个特定的执行计划.stored outline是一种遗留技术用于提供执行 ...

  3. 查询sql执行计划_使用SQL执行计划进行查询性能调整

    查询sql执行计划 In the previous articles of this series (see the index at bottom), we went through many as ...

  4. oracle sql 执行计划分析_Oracle SQL调优系列之看懂执行计划explain

    1.文章写作前言简介 SQL调优系列博客链接:SQL调优专栏 之前曾经拜读过<收获,不止sql调优>一书,此书是国内DBA写的一本很不错的调优类型的书,是一些很不错的调优经验的分享.虽然读 ...

  5. SAP HANA SQL执行计划(SAP HANA SQL explain plan)

    SAP HANA SQL执行计划: 方法一:直接写执行语句 explain  plan  SET STATEMENT_NAME = 'select_emp' FOR select ename,sal, ...

  6. oracle的sql执行计划语句,最权威Oracle获取SQL语句执行计划大全

    该文档为根据相关资料整理.总结而成,主要讲解Oracle数据库中,获取SQL语句执行计划的最权威.最正确的方法.步骤,此外,还详细说明了每种方法中可选项的意义及使用方法,以方便大家和自己日常工作中查阅 ...

  7. Oracle中SQL调优(SQL TUNING)之最权威获取SQL执行计划大全

    该文档为根据相关资料整理.总结而成,主要讲解Oracle数据库中,获取SQL语句执行计划的最权威.最正确的方法.步骤,此外,还详细说明了每种方法中可选项的意义及使用方法,以方便大家和自己日常工作中查阅 ...

  8. 把复杂交给我们,把简单还给你丨TiVP 让 SQL 执行计划可视化

    ​> 通过 TiDB Hackathon 重新认识了自己的潜力.当你想做一件事情,你会发现自己特别专注在上面,然后一些你觉得不会成功.不可能的事情也会变得可能. --TiVP 团队 在刚刚结束的 ...

  9. sql执行组件是灰色的_如何分析SQL执行计划图形组件

    sql执行组件是灰色的 In the previous articles of this series, SQL Server Execution Plans overview and SQL Ser ...

最新文章

  1. spring scope=prototype 学习笔记
  2. python处理表格数据-python读取word 中指定位置的表格及表格数据
  3. 《大数据》杂志——大数据技术发展的十个前沿方向(中)
  4. pyplot 画多个图时搅合到了一起_这些认数字游戏,宝宝最喜欢,家长可以和宝宝一起玩...
  5. jpa 公共字段顺序_临沂沂水景观花箱销售公司公共使用
  6. 双指针算法基础——输出单词
  7. 管家婆普及版_昆明逸马软件 — 管家婆服装普及版
  8. 爱忘事、不自律,有了这5款时间管理工具,堪比“罗志祥”!
  9. ffmpeg -比特率,帧率和文件大小
  10. 如何在手机下载微博APP中视频?【最新有效方法】
  11. 用了五年 VS Code ,我决定换成 JetBrains……
  12. 论文笔记:DeepReID: Deep Filter Pairing Neural Network for Person Re-Identification
  13. excel怎么拆分表格
  14. 【装机吧】电脑上网慢的解决方法
  15. 西电和杭电计算机考研,名师张雪峰:中国有四个电子科技大学,有一个很特别,考研要注意...
  16. Python “ValueError: incomplete format” upon print(“stuff %” % “thingy”) 解决方法
  17. Codeforces Round #742 (Div. 2) C. Carrying Conundrum
  18. [工具] UltraEdit使用技巧汇总
  19. 2021 大数据白皮书(中国信通院)
  20. 将Spine动画导入unity

热门文章

  1. 2018总结以及2019展望
  2. 招聘 | 广州心理学公司招聘招商经理
  3. 搭建8086汇编语言学习环境——dosbox
  4. 移动端APP渲染原理
  5. Linux抢占式调度简介(转)
  6. 类的继承和派生——求圆柱表面积
  7. linux多重引导工具,不同操作环境下,如何制作多重引导USB?
  8. Redis配置文件redis-conf详解
  9. 万家乐“天工系列”预热AWE2019 厨卫电器呈现新形态
  10. ESD-CAN安装调试笔记