特别说明:程序员
一、 本文只是面对数据库应用开发的程序员,不适合专业DBA,DBA在数据库性能优化方面须要了解更多的知识;数据库
二、 本文许多示例及概念是基于Oracle数据库描述,对于其它关系型数据库也能够参考,但许多观点不适合于KV数据库或内存数据库或者是基于SSD技术的数据库;缓存
三、 本文未深刻数据库优化中最核心的执行计划分析技术。性能优化
读者对像:服务器
开发人员:若是你是作数据库开发,那本文的内容很是适合,由于本文是从程序员的角度来谈数据库性能优化。网络
架构师:若是你已是数据库应用的架构师,那本文的知识你应该清楚90%,不然你多是一个喜欢折腾的架构师。架构
DBA(数据库管理员):大型数据库优化的知识很是复杂,本文只是从程序员的角度来谈性能优化,DBA除了须要了解这些知识外,还须要深刻数据库的内部体系架构来解决问题。ide
引言函数
在网上有不少文章介绍数据库优化知识,可是大部份文章只是对某个一个方面进行说明,而对于咱们程序员来讲这种介绍并不能很好的掌握优化知识,由于不少介绍只是对一些特定的场景优化的,因此反而有时会产生误导或让程序员感受不明白其中的奥妙而对数据库优化感受很神秘。性能
不少程序员老是问如何学习数据库优化,有没有好的教材之类的问题。在书店也看到了许多数据库优化的专业书籍,可是感受更可能是面向DBA或者是PL/SQL开 发方面的知识,我的感受不太适合普通程序员。而要想作到数据库优化的高手,不是花几周,几个月就能达到的,这并非由于数据库优化有多高深,而是由于要作 好优化一方面须要有很是好的技术功底,对操做系统、存储硬件网络、数据库原理等方面有比较扎实的基础知识,另外一方面是须要花大量时间对特定的数据库进行实 践测试与总结。
做为一个程序员,咱们也许不清楚线上正式的服务器硬件配置,咱们不可能像DBA那样专业的对数据库进行各类实践测试与总结,但咱们都应该很是了解咱们SQL的业务逻辑,咱们清楚SQL中访问表及字段的数据状况,咱们其实只关心咱们的SQL是否能尽快返回结果。那程序员如何利用已知的知识进行数据库优化?如何能快速定位SQL性能问题并找到正确的优化方向?
面对这些问题,笔者总结了一些面向程序员的基本优化法则,本文将结合实例来坦述数据库开发的优化知识。
要正确的优化SQL,咱们须要快速定位能性的瓶颈点,也就是说快速找到咱们SQL主要的开销在哪里?而大多数状况性能最慢的设备会是瓶颈点,以下载时网络速度可能会是瓶颈点,本地复制文件时硬盘可能会是瓶颈点,为何这些通常的工做咱们能快速确认瓶颈点呢,由于咱们对这些慢速设备的性能数据有一些基本的认识,如网络带宽是2Mbps,硬盘是每分钟7200转等等。所以,为了快速找到SQL的性能瓶颈点,咱们也须要了解咱们计算机系统的硬件基本性能指标,下图展现的当前主流计算机性能指标数据。
从图上能够看到基本上每种设备都有两个指标:
延时(响应时间):表示硬件的突发处理能力;
带宽(吞吐量):表明硬件持续处理能力。
从上图能够看出,计算机系统硬件性能从高到代依次为:
CPU——Cache(L1-L2-L3)——内存——SSD硬盘——网络——硬盘
因为SSD硬盘还处于快速发展阶段,因此本文的内容不涉及SSD相关应用系统。
根据数据库知识,咱们能够列出每种硬件主要的工做内容:
CPU及内存:缓存数据访问、比较、排序、事务检测、SQL解析、函数或逻辑运算;
网络:结果数据传输、SQL请求、远程数据库访问(dblink);
硬盘:数据访问、数据写入、日志记录、大数据量排序、大表链接。
根据当前计算机硬件的基本性能指标及其在数据库中主要操做内容,能够整理出以下图所示的性能基本优化法则:
这个优化法则概括为5个层次:
一、 减小数据访问(减小磁盘访问)
二、 返回更少数据(减小网络传输或磁盘访问)
三、 减小交互次数(减小网络传输)
四、 减小服务器CPU开销(减小CPU及内存开销)
五、 利用更多资源(增长资源)
因为每一层优化法则都是解决其对应硬件的性能问题,因此带来的性能提高比例也不同。传统数据库系统设计是也是尽量对低速设备提供优化方法,所以针对低速设备问题的可优化手段也更多,优化成本也更低。咱们任何一个SQL的性能优化都应该按这个规则由上到下来诊断问题并提出解决方案,而不该该首先想到的是增长资源解决问题。
如下是每一个优化法则层级对应优化效果及成本经验参考:
优化法则 |
性能提高效果 |
优化成本 |
减小数据访问 |
1~1000 |
低 |
返回更少数据 |
1~100 |
低 |
减小交互次数 |
1~20 |
低 |
减小服务器CPU开销 |
1~5 |
低 |
利用更多资源 |
@~10 |
高 |
接下来,咱们针对5种优化法则列举经常使用的优化手段并结合实例分析。
数据块是数据库中数据在磁盘中存储的最小单位,也是一次IO访问的最小单位,一个数据块一般能够存储多条记录,数据块大小是DBA在建立数据库或表空间时指定,可指定为2K、4K、8K、16K或32K字节。下图是一个Oracle数据库典型的物理结构,一个数据库能够包括多个数据文件,一个数据文件内又包含多个数据块;
ROWID是每条记录在数据库中的惟一标识,经过ROWID能够直接定位记录到对应的文件号及数据块位置。ROWID内容包括文件号、对像号、数据块号、记录槽号,以下图所示:
数据库索引的原理很是简单,但在复杂的表中真正能正确使用索引的人不多,即便是专业的DBA也不必定能彻底作到最优。
索引会大大增长表记录的DML(INSERT,UPDATE,DELETE)开销,正确的索引可让性能提高100,1000倍以上,不合理的索引也可能会让性能降低100倍,所以在一个表中建立什么样的索引须要平衡各类业务需求。
索引常见问题:
索引有哪些种类?
常见的索引有B-TREE索引、位图索引、全文索引,位图索引通常用于数据仓库应用,全文索引因为使用较少,这里不深刻介绍。B-TREE索引包括不少扩展类型,如组合索引、反向索引、函数索引等等,如下是B-TREE索引的简单介绍:
B-TREE索引也称为平衡树索引(Balance Tree),它是一种按字段排好序的树形目录结构,主要用于提高查询性能和惟一约束支持。B-TREE索引的内容包括根节点、分支节点、叶子节点。
叶子节点内容:索引字段内容+表记录ROWID
根节点,分支节点内容:当一个数据块中不能放下全部索引字段数据时,就会造成树形的根节点或分支节点,根节点与分支节点保存了索引树的顺序及各层级间的引用关系。
一个普通的BTREE索引结构示意图以下所示:
若是咱们把一个表的内容认为是一本字典,那索引就至关于字典的目录,以下图所示:
图中是一个字典按部首+笔划数的目录,至关于给字典建了一个按部首+笔划的组合索引。
一个表中能够建多个索引,就如一本字典能够建多个目录同样(按拼音、笔划、部首等等)。
一个索引也能够由多个字段组成,称为组合索引,如上图就是一个按部首+笔划的组合目录。
SQL什么条件会使用索引?
当字段上建有索引时,一般如下状况会使用索引:
INDEX_COLUMN = ?
INDEX_COLUMN > ?
INDEX_COLUMN >= ?
INDEX_COLUMN < ?
INDEX_COLUMN <= ?
INDEX_COLUMN between ? and ?
INDEX_COLUMN in (?,?,...,?)
INDEX_COLUMN like ?||'%'(后导模糊查询)
T1. INDEX_COLUMN=T2. COLUMN1(两个表经过索引字段关联)
SQL什么条件不会使用索引?
查询条件 |
不能使用索引缘由 |
INDEX_COLUMN <> ? INDEX_COLUMN not in (?,?,...,?) |
不等于操做不能使用索引 |
function(INDEX_COLUMN) = ? INDEX_COLUMN + 1 = ? INDEX_COLUMN || 'a' = ? |
通过普通运算或函数运算后的索引字段不能使用索引 |
INDEX_COLUMN like '%'||? INDEX_COLUMN like '%'||?||'%' |
含前导模糊查询的Like语法不能使用索引 |
INDEX_COLUMN is null |
B-TREE索引里不保存字段为NULL值记录,所以IS NULL不能使用索引 |
NUMBER_INDEX_COLUMN='12345' CHAR_INDEX_COLUMN=12345 |
Oracle在作数值比较时须要将两边的数据转换成同一种数据类型,若是两边数据类型不一样时会对字段值隐式转换,至关于加了一层函数处理,因此不能使用索引。 |
a.INDEX_COLUMN=a.COLUMN_1 |
给索引查询的值应是已知数据,不能是未知字段值。 |
注: 通过函数运算字段的字段要使用可使用函数索引,这种需求建议与DBA沟通。 有时候咱们会使用多个字段的组合索引,若是查询条件中第一个字段不能使用索引,那整个查询也不能使用索引 如:咱们company表建了一个id+name的组合索引,如下SQL是不能使用索引的 Select * from company where name=? Oracle9i后引入了一种index skip scan的索引方式来解决相似的问题,可是经过index skip scan提升性能的条件比较特殊,使用很差反而性能会更差。
|
咱们通常在什么字段上建索引?
这是一个很是复杂的话题,须要对业务及数据充分分析后再能得出结果。主键及外键一般都要有索引,其它须要建索引的字段应知足如下条件:
1、字段出如今查询条件中,而且查询条件可使用索引;
2、语句执行频率高,一天会有几千次以上;
3、经过字段条件可筛选的记录集很小,那数据筛选比例是多少才适合?
这个没有固定值,须要根据表数据量来评估,如下是经验公式,可用于快速评估:
小表(记录数小于10000行的表):筛选比例<10%;
大表:(筛选返回记录数)<(表总记录数*单条记录长度)/10000/16
单条记录长度≈字段平均内容长度之和+字段数*2
如下是一些字段是否须要建B-TREE索引的经验分类:
字段类型 |
常见字段名 |
|
须要建索引的字段 |
主键 |
ID,PK |
外键 |
PRODUCT_ID,COMPANY_ID,MEMBER_ID,ORDER_ID,TRADE_ID,PAY_ID |
|
有对像或身份标识意义字段 |
HASH_CODE,USERNAME,IDCARD_NO,EMAIL,TEL_NO,IM_NO |
|
索引慎用字段,须要进行数据分布及使用场景详细评估 |
日期 |
GMT_CREATE,GMT_MODIFIED |
年月 |
YEAR,MONTH |
|
状态标志 |
PRODUCT_STATUS,ORDER_STATUS,IS_DELETE,VIP_FLAG |
|
类型 |
ORDER_TYPE,IMAGE_TYPE,GENDER,CURRENCY_TYPE |
|
区域 |
COUNTRY,PROVINCE,CITY |
|
操做人员 |
CREATOR,AUDITOR |
|
数值 |
LEVEL,AMOUNT,SCORE |
|
长字符 |
ADDRESS,COMPANY_NAME,SUMMARY,SUBJECT |
|
不适合建索引的字段 |
描述备注 |
DESCRIPTION,REMARK,MEMO,DETAIL |
大字段 |
FILE_CONTENT,EMAIL_CONTENT |
如何知道SQL是否使用了正确的索引?
简单SQL能够根据索引使用语法规则判断,复杂的SQL很差办,判断SQL的响应时间是一种策略,可是这会受到数据量、主机负载及缓存等因素的影响,有时数据全在缓存里,可能全表访问的时间比索引访问时间还少。要准确知道索引是否正确使用,须要到数据库中查看SQL真实的执行计划,这个话题比较复杂,详见SQL执行计划专题介绍。
索引对DML(INSERT,UPDATE,DELETE)附加的开销有多少?
这个没有固定的比例,与每一个表记录的大小及索引字段大小密切相关,如下是一个普通表测试数据,仅供参考:
索引对于Insert性能下降56%
索引对于Update性能下降47%
索引对于Delete性能下降29%
所以对于写IO压力比较大的系统,表的索引须要仔细评估必要性,另外索引也会占用必定的存储空间。