数据库规范与SQL调优

数据库设计规范章节,依旧以《阿里巴巴Java开发手册》为原型进行修正和完善。 MySQL规约 (一) 建表规约 (二) 索引规约 (三) SQL规约 (四) ORM规约 (一) 建表规约 1. 【强制】 表达是与否概念的字段,必须使用is_xxx的方式命名,数据类型是 unsignedtinyint( 1表示是,0表示否),此规则同样适用于odps建表。 说明:任何字段如果为非负数,必须是unsigned。 2. 【强制】 表名、字段名必须使用小写字母或数字;禁止出现数字开头,禁止两个下划 线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名 称需要慎重考虑。 正例: getter_admin task_config level3_name 反例: GetterAdmin taskConfig level_3_name 3. 【强制】 表名不使用复数名词。 说明:表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于DO类名 也是单数形式,符合表达习惯。 4. 【强制】 禁用保留字,如desc、range、match、delayed等,请参考MySQL官方保留 字。 5. 【强制】 唯一索引名为uk 字段名;普通索引名则为idx 字段名。 说明:uk 即 unique key;idx 即index的简称。 6. 【强制】 小数类型为decimal,禁止使用float和double。 说明:float和double在存储的时候,存在精度损失的问题,很可能在值的比较时,得 到不正确的结果。如果存储的数据范围超过decimal的范围,建议将数据拆成整数和 小数分开存储。 7. 【强制】 如果存储的字符串长度几乎相等,使用char定长字符串类型。 8. 【强制】 varchar是可变长字符串,不预先分配存储空间,长度不要超过5000,如果 存储长度大于此值,定义字段类型为text,独立出来一张表,用主键来对应,避免影响 其它字段索引效率。 9. 【强制】 表必备字段:id, creation_time,creator,modified_time,modifier,valid。 说明:其中id必为主键,类型为unsigned bigint、单表时自增、步长为1。 gmt_create,gmt_modified的类型均为date_time类型。 10. 【强制⭐】 所有表必须有主键,主键必须保证有序。Mysql的锁,锁的是主键索引树,没主 键会导致所有操作都是锁全表。而主键无序,会导致频繁页分裂,大大降低更新速度。 11. 【推荐】 表的命名最好是加上“业务名称_表的作用”。 正例: tiger_task / tiger_reader / mpp_config 12. 【推荐】 库名与应用名称尽量一致。 13. 【推荐】 如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。 14. 【推荐】 字段允许适当冗余,以提高性能,但是必须考虑数据同步的情况。冗余字段 应遵循: 不是频繁修改的字段。 不是varchar超长字段,更不能是text字段。 正例:商品类目名称使用频率高,字段长度短,名称基本一成不变,可在相关联的表中冗余存储 类目名称,避免关联查询。 15. 【推荐】一般情况下, 单表行数超过500万行或者单表容量超过2GB,才推荐进行分 库分表。 如果某张表字段特别多,或者字段比较大,则需要提前进行。按照以前的经验, 110个字段的表,在数据量超过100万时,已经会比较严重地拖慢整体性能了。 说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分 表。 16.【参考】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是 提升检索速度。 1 正例:人的年龄用 unsigned tinyint (表示范围 0-255 ,人的寿命不会超过 255 岁);海龟就 必须是 smallint ,但如果是太阳的年龄,就必须是 int ;如果是所有恒星的年龄都加起来,那么 就必须使用 bigint (二) 索引规约 1. 【强制】 业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。 说明:不要以为唯一索引影响了insert速度,这个速度损耗可以忽略,但提高查找速 度是明显的;另外,即使在应用层做了非常完善的校验和控制,只要没有唯一索引, 根据墨菲定律,必然有脏数据产生。 2. 【强制】 超过三个表禁止join。需要join的字段,数据类型保持绝对一致;多表关联查 询时,保证被关联的字段需要有索引。 说明:即使双表join也要注意表索引、SQL性能。 3. 【强制】 在varchar字段上建立索引时,必须指定索引长度,没必要对全字段建立索 引,根据实际文本区分度(离散度)决定索引长度。 说明:索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为20的索 引,区分度会高达90%以上,可以使用count(distinct left(列名,索引长度))/count(*) 的区分度来确定。 4. 【强制】 页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。 说明:索引文件具有B-Tree的最左前缀匹配特性,如果左边的值未确定,那么无法 使用此索引。 5. 【强制】 对于联合索引左边第一个能够覆盖到的索引列,不要再单独添加索引。 6.【强制】创建索引的列,不要有Null值,查询Null值无法走索引。 7. 【推荐】 如果有order by的场景,请注意利用索引的有序性。order by最后的字段是 组合索引的一部分,并且放在索引组合顺序的最后,避免出现file_sort的情况,影响查 询性能。 1 正例: where a=? and b=? order by c; 索引: a_b_c 2 反例:索引中有范围查找,那么索引有序性无法利用,如: WHERE a>10 ORDER BY b; 索引 a_ b 无法排序。 8. 【推荐】 利用覆盖索引来进行查询操作,来避免回表操作。 说明:如果一本书需要知道第11章是什么标题,会翻开第11章对应的那一页吗?目 录浏览一下就好,这个目录就是起到覆盖索引的作用。 1 正例:能够建立索引的种类:主键索引、唯一索引、普通索引,而覆盖索引是一种查询的一种效 果,用 explain 的结果, extra 列会出现: using index 9. 【推荐】 利用延迟关联或者子查询优化超多分页场景。 说明:MySQL并不是跳过offset行,而是取offset+N行,然后返回放弃前offset行, 返回N行,那当offset特别大的时候,效率就非常的低下,要么控制返回的总页数, 要么对超过特定阈值的页数进行SQL改写。 10. 【推荐】 SQL性能优化的目标:至少要达到 range级别,要求是ref级别,如果可以 是consts最好。 说明: 1)consts单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取 到数据。 2)ref指的是使用普通的索引( normal index)。 3)range对索引进行范围检索。 11. 【推荐】 建组合索引的时候,区分度最高的在最左边(最左匹配原则)。 12.【参考】创建索引时避免有如下极端误解: 误认为一个查询就需要建一个索引。 误认为索引会消耗空间、严重拖慢更新和新增速度。 误认为唯一索引一律需要在应用层通过“先查后插”方式解决。 误认为索引建了就一定会走索引。 误认为索引一定能提高查询效率。 (三) SQL规约 1. 【强制】 不要使用count(列名)或count(常量)来替代count( *) ,count( *) 就是 SQL92定义的标准统计行数的语法,跟数据库无关,跟NULL和非NULL无关。 说明:count(*)会统计值为NULL的行,而count(列名)不会统计此列为NULL值的 行。 2. 【强制】 count(distinct column)计算该列除NULL之外的不重复数量。注意 count(distinct column1, column2)如果其中一列全为NULL,那么即使另一列有不同的 值,也返回为0。 1 正例:先快速定位需要获取的 id 段,然后再关联: SELECT a.* FROM 1 a, (select id from 1 where 条件 LIMIT 100000,20 ) b wh ere a.id=b.id 2 反例: explain 表的结果, type=index ,索引物理文件全扫描,速度非常慢,这个 index 级别 比较 range 还低,与全表扫描是小巫见大巫。 1 1 正例:如果 where a=? and b=? a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可。 说明:存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。如: wherea >?and b=? 那么即使 a 的区分度更高,也必须把 b 放在索引的最前列。 2 3. 【强制】 当某一列的值全是NULL时,count(column)的返回结果为0,但 sum(column)的返回结果为NULL,因此使用sum(column)时需注意NPE问题。 1 正例:可以使用如下方式来避免 sum NPE 问题: SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM table; 4. 【强制】 使用ISNULL()来判断是否为NULL值。注意:NULL与任何值的直接比较都为 NULL。 说明: 1) NULL<>NULL的返回结果是NULL,而不是false。 2) NULL=NULL的返回结果是NULL,而不是true。 3) NULL<>1的返回结果是NULL,而不是true。 5. 【强制】 在代码中写分页查询逻辑时,若count为0应直接返回,避免执行后面的分页 语句。 6.【强制⭐】不得使用外键与级联,一切外键概念必须在应用层解决。 说明:(概念解释)学生表中的student_id是主键,那么成绩表中的student_id则为 外键。如果更新学生表中的student_id,同时触发成绩表中的student_id更新,则为 级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更 新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。 7. 【强制】 禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。 8. 【强制】 数据订正时,删除和修改记录时,要先select,避免出现误删除,确认无误 才能执行更新语句。 9. 【强制】 删除数据时,禁止“delete from Table where Condition”。必须先查询出待删除的 数据,然后根据数据ID删除。程序如果没有传入Condition的条件,最终结果不是不删除数据, 而是删除所有数据,必须避免。 10. 【推荐】 in操作能避免则避免,若实在避免不了,需要仔细评估in后边的集合元素数 量,控制在1000个之内。 11.【参考】如果有全球化需要,所有的字符存储与表示,均以utf-8编码,那么字符计 数方法 注意: 说明: SELECT LENGTH("轻松工作");返回为12 SELECT CHARACTER_LENGTH("轻松工作");返回为4 如果要使用表情,那么使用utfmb4来进行存储,注意它与utf-8编码的区别。 12.【参考】TRUNCATETABLE比 DELETE速度快,且使用的系统和事务日志资源少, 但TRUNCATE无事务且不触发trigger,有可能造成事故,故不建议在开发代码中使用此 语句。 说明:TRUNCATE TABLE在功能上与不带 WHERE子句的 DELETE语句相同。 (四) ORM规约 1. 【强制】 在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确 写明。 说明: 1)增加查询分析器解析成本。 2)增减字段容易与resultMap配置不一致。 2. 【强制】 POJO类的boolean属性不能加is,而数据库字段必须加is_,要求在 resultMap中进行字段与属性之间的映射。 说明:参见定义POJO类以及数据库字段定义规定,在sql.xml增加映射,是必须的。 3. 【强制】 不要用resultClass当返回参数,即使所有类属性名与数据库字段一一对应, 也需要定义;反过来,每一个表也必然有一个与之对应。 说明:配置映射关系,使字段与DO类解耦,方便维护。 4. 【强制】 xml配置中参数注意使用:#{},#param#不要使用${}此种方式容易出现SQL 注入。 5. 【强制】 iBATIS自带的queryForList(String statementName,int start,int size)不推荐 使用(它是基于内存的分页方式,存在深分页问题)。 说明:其实现方式是在数据库取到statementName对应的SQL语句的所有记录,再 通过subList取start,size的子集合,线上因为这个原因曾经出现过OOM。 1 正例:在 sqlmap.xml 中引入 #start#, #size# 2 Map map = new HashMap(); 3 map.put("start", start); 4 map.put("size", size); 6. 【强制】 不允许直接拿HashMap与Hashtable作为查询结果集的输出。 7. 【强制】 更新数据表记录时,必须同时更新记录对应的更新人与更新时间。对于拿不 到更新人的场景,可以统一使用保留字”system“,不要留空。 8. 【推荐】 不要写一个大而全的数据更新接口,传入为POJO类,不管是不是自己的目 标更新字段,都进行update table set c1=value1,c2=value2,c3=value3;这是不对的。 执行SQL时,尽量不要更新无改动的字段,一是易出错;二是效率低;三是binlog增加 存储。 9.【推荐】编写查询语句”select column1,column2,... from table where XXX“时,字 段的个数如果比较少,可以适当冗余。 试想一个场景:代码中同时存在针对某个实体的查询方法,它们的入参和返回值完 全一样,只有条件不同,这很容易被误解两个方法的返回值包含的属性值也一致。但如 果底层的实现中,select 的列名不一样,则很容易导致空指针。 思考:只查询需要的字段,数据库只需要返回用得到的字段即可,但方法可能有歧 义导致误用。查询全量字段,会要求数据库返回更多的信息,不过方法含义明确,不会 因为漏了字段值而导致问题。 10.【参考】@Transactional事务不要滥用。事务会影响数据库的QPS,另外使用事务 的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修 正等。 11.【参考】中的compareValue是与属性值对比的常量,一般是数字,表示相等时带上 此条件;表示不为空且不为null时执行;表示不为null值时执行。 12.【参考】作为更新场景的补充,为了不全量更新,一般会先判断传入的值是否为空,不为空 则更新。此时如果确实想设置某些字段的值为空,则无法实现。这种情况下,建议单独写一个设 置空值的方法,而不是通过一个全量更新的方法实现。 数据库层的调优 这里主要是一些与数据库调优相关的内容,包括数据库服务器,SQL分析等。 数据库服务器的性能 服务器端的分析集中在这几个方向上:硬件资源。数据库架构,以及数据库实例参数优化。 硬件资源分析 1,使用top命令查看CPU负载。 2,使用free命令查看内存使用情况。 3,使用iostat工具查看磁盘I/O使用情况。 4,使用vmstat命令查看系统的负载情况。 5,使用perf top命令查看系统热点情况。 6,使用nmon工具监控系统一段时间的整体情况。 使用腾讯云数据库时,也可以用腾讯云提供的相关工具。 如果发现数据库主机的CPU、 I/O、内存等使用率很高,那么原因有两种: 1,数据库实例存在性能瓶颈。 2,实例所在的机器硬件本身存在问题(可能性比较小,也容易排除) 。 数据库架构分析 目前公司主要是云数据库架构,依托腾讯云本身的能力,这一块的架构分析可以略过或找腾 讯云的售后进行支持。 服务器调优 数据库服务器调优。可以结合业务场景对数据库参数进行优化。以下是一个优化示例。 例如:有一个密集交易型数据库服务器配置如下: CPU:4路8核。 内存:256G。 磁盘阵列:1T。 那么,推荐的参数设置如下: 识别慢SQL 目前使用腾讯云数据库,它们提供了数据库监控以及慢日志监控等功能,可以直接拿到慢 SQL。接下来按并发度对这些SQL进行分类: 1,并发非常高。SQL特征:SQL的条数很少(按5%统计),但是执行频率非常高,甚至达到 每秒上百次,只要一慢,系统很可能瘫痪。 优化级别:最优先处理。 优化方向: 对SQL本身进行优化,调到最优。 对发起SQL请求的应用进行优化,减少执行次数。包括使用缓存,多次请求合并为一次 等等。 2,并发一般 。SQL特征:占大多数(按80%统计),如果有慢的,对系统整体稳定性影响不 大,但是会造成局部的某些操作慢。 优化级别:次优先处理。 优化方向:对SQL本身进行优化,综合考虑索引,SQL改写等方式。 3,并发很少特别慢。SQL特征:数量少(按15%统计),往往是很复杂的查询,可能一天就 执行几次,对系统整体影响不大,但是优化难度很大。 优化级别:最后处理 。 优化方向:对SQL本身进行优化,同时可以对这类SQL有一定容忍度。 阻塞与死锁分析 死锁与阻塞是最为常见的两个性能杀手。可以用一些SQL来观测SQL的执行情况: 【show processlist】:此命令可以查看服务端线程状态。利用这些信息,可以找到耗时比 较长的环节并对其进行优化。还可以对阻塞的线程进行kill。使用【show processlist】会列出一 系列信息,其含义如下: 1,id。这是线程的唯一标记,【 kill {id}】可以终止该线程。 2,user。这个字段表示启动该线程的用户。 3,host。这个字段表示发起该连接的host。 4,db。这个字段表示操作的数据库是哪个。 5,command。表示操作的命令,包括SQL语句也是一种命令。 6,time。表示该操作持续了多久。 7,state。表示该线程当前处于什么状态。 8,info。包含前100个字符的Sql语句。如果想查看完整SQL,可以使用【show full processlist】。 【show status】:此命令可以查看Mysql服务器的运行状态,重启后状态信息会被清空。 还可以带上作用域,比如【 show global status】表示查看全局状态。 【show engine】:此命令用于查看存储引擎的状态。包括事务持有的表锁、行锁信息;事 务的锁等待情况;线程信号量等待;文件IO 请求;buffer pool 统计信息等。 死锁的特征: 1,死锁是相互堵塞。 2,数据库自动识别死锁并解锁。 3,SQL日志中会记录死锁信息。 避免数据库死锁需要采取以下措施: 1,减少事务的数量:减少并发事务的数目是避免死锁的首要步骤。 2,统一事务处理:尽量将对同一数据库表的更新操作放在同一个事务中,以减少出现死锁 的机会。 3,按照相同的顺序访问资源: 所有事务都按照相同的顺序来访问资源,以避免出现交叉等 待的情况。 4,使用低级别锁:尽可能使用低级别锁,例如行锁而不是表锁,以减少出现死锁的机会。 5,缩短锁定时间:尽量缩短锁定时间,并在完成后立即释放锁,以便其他事务可以及时访 问相关资源。 6,定期清理无用的锁:通过定期清理不再使用的锁,可以避免死锁的产生。 7,使用数据库提供的工具检测死锁:大部分数据库系统都提供了用于检测死锁的工具或 API,可以使用这些工具或API来监视数据库的死锁情况,并及时采取措施。 8,优化数据库设计和查询:优化数据库表格设计和查询语句,包括索引的建立、SQL 查询 语句的优化等,可以降低数据库死锁的风险。 阻塞的特征: 1,阻塞的SQL不会互相影响,它是单向的。 2,数据库无法自动识别阻塞并进行处理。 3,SQL日志中不会记录阻塞信息,表现形式只会是某SQL执行时间超长。 处理阻塞需要人工介入,大致方向有两个:减少并发操作,以及减少慢SQL。具体措施: 1,使用低级别锁:尽量使用低级别的行级别锁而不是高级别的表级别锁,以减少对资源的 阻塞。 2,使用索引: 通过在 WHERE 子句中使用索引列可以极大地加快 SQL 查询速度,从而减 少对相关资源的占用时间。 3,分批处理数据:如果需要处理大量数据,可以将其分批处理,以便避免一次性占用过多 的资源。 4,避免长事务和长查询:长时间运行的事务和长时间运行的查询会持续占用资源,导致其 他操作被阻塞。应该避免这种情况并定期清理无用的事务和查询。 5,调整数据库参数: 可以通过调整数据库参数来优化 SQL 执行性能,例如增加缓存大 小、调整线程池大小等。 6,定期优化数据库: 定期检查和优化数据库表格结构、索引、存储引擎等,可以提高数据 库的性能和响应速度。 7,合理设计 SQL 查询语句:合理的 SQL 查询语句设计可以有效地降低系统的负载和阻塞 风险。 SQL执行流程分析 对SQL执行流程所涉及的每一个环节进行优化,最终结果就是最优的。按照这个思路,先来 看看SQL执行流程: 客户端层的优化: 1,减少连接消耗:多次请求合并为一次,合理设置连接池的大小和连接施放时间。注意连 接数并不是越多越好。连接过多除了导致资源消耗增加外,还会额外引入更多的线程切换成本。 这里提供一个经验公式用于推断连接数的个数:【连接数=CPU核心数*2+1】 2,降低查询频率:合理使用缓存,将多条请求合并为同一条。 Mysql连接层的优化: 1,增加连接数。 2,及时释放不使用的连接。 3,Mysql相关参数调优。 查询缓存层的优化:查询缓存层设计得比较纠结,默认已经禁用了,仅当读远远大于写的场 景才有用。这一层的优化可以忽略。 SQL解析器和预处理器:这两个组件的作用是生成一颗正确的语法树。它的使用对用户来说 是黑盒,无法干预。 查询优化器:这一层会根据语法树生成多个执行计划,并进行优选。对SQL的优化,主要集 中在这里。 执行引擎:这一层根据执行计划进行调度,对用户来说,只能通过干预执行计划来间接干预 执行引擎的工作。 存储引擎层的优化:根据业务场景选择合适的存储引擎。例如归档表往往对事务的要求比较 弱,又以读为主,可以设置使用Mysiam引擎。再比如一些高频使用的小表可直接使用Memory 引擎常驻内存。 分析执行计划 在SQL语句前面加上explain并执行,可以列出该sql语句的执行计划,其语法格式如下: explain {sql}】。在MySQL 5.6.3 以前的版本,只能分析SELECT。MySQL5.6.3以后就可以 分析update、delete和insert了。 一个典型的执行计划信息如下: 可以看到上述SQL语句出现了多行执行计划信息。这些执行计划表达的含义就是该SQL执行 时需要经过的步骤和其它信息,每条数据就是执行计划的一个步骤。接下来详细看看每个字段的 含义。 id字段: id表示SQL语句执行的顺序。数字越大的执行计划越先被执行。如果数字相同,则按从上往 下的顺序依次执行。例如,子查询的优先级会高于外层查询。 如果是并列的查询,没有父子之分,查询优化器会自动进行优化:它会选用笛卡尔积相对较 小的结果作为中间结果,从而使得它们先被执行。Mysql的优化器是基于开销的,更小的中间结 果消耗更少的资源。此外,这也要求我们在做优化时,选择更小的表作为驱动表。 select_type字段: 这个字段表示查询的类型。查询类型很多,这里列举几个常见的: 1,simple。simple表示普通查询,它不包含子查询也不包含union查询。 2,primary。如果一个SQL语句包含子查询,那么最外层的查询类型就是primary。 3,subquery。如果一个SQL语句包含子查询,那么最内层的子查询类型就是subquery。 4,derived。意思是衍生查询。如果在得到最终结果之前,一个查询操作用到了临时表,那 么该查询的类型就是derived。比如一个union查询,Mysql会先执行union右边的语句,得到中 间结果放到临时表里,然后执行union左边的语句并与刚刚的临时表关联,其类型就是derived。 5,union。使用了union查询时,出现在union右边的查询会被标记为union类型。 6,union result。这种类型表示所有的union语句执行完毕后产生的中间结果。即使有多个 union查询,union result也只有一项。 table字段: table字段表示查询过程中用到的表,包括普通的表和中间结果的表。普通表就不用细说 了,显示哪个表名,就表示针对那张表的查询。在union查询时,有时候table字段会显示类似 】这种值。这个值表示把ID为1,3,4的查询产生的结果集,作为当前查询所使 用的table。 type字段: 这个字段很关键,其含义是连接类型。不同的连接类型其执行效率差别很大,下面按性能由 高到低的顺序排列它们,并分别解释每一项的含义: 1,system。system表示查询系统表,表里只有一行记录。这个是最快的,然而是一种特 例,没有讨论的价值,基本不会出现。 2,const。它表示通过索引一次就找到了。通过一次索引就能找到数据的情况只有一种: 通过唯一索引查询数据,并且只查询到了一条数据。例如【 select * from t_table where id=1】。 3,eq_ref。它通常出现在join语句中。对于前表中的每个索引列,都只能匹配到后表中的 唯一结果,此时后表的连接类型就是eq_ref。既然只能匹配到唯一结果,那多半是唯一性的索引 了。一般而言,join语句后面on的条件是唯一索引时会匹配到这种类型。 4,ref。它也是通常出现在join语句中。如果使用的不是唯一性的索引,那么前表中的索引 列的值可能对应后表中的多个结果,此时后表的连接类型就是ref。一般而言,join语句后面on 的条件是非唯一索引时会匹配到这种类型。 5,range。range表示使用索引进行范围查找。通常【 in (...),between... and ...】或者 【where column_a>{number}】这种类型的语句会匹配到range类型。 6,index。index的意思是走索引的全表扫描。将所有索引扫一遍去找对应结果,这个已经 很慢了。遇到index类型的查询,说明需要优化。 误解:很多人认为“index”表示走索引的查询,这个理解大大的错,index的性能很低,说不 定还不如顺序的全表扫描,至少不用随机读。 7,all。all是最糟糕的情况,它表示不走索引全表扫描。我们应该尽量避免all。 8,Null。这是一种特殊情况。当前查询不需要查询表时,其type会是Null。比如获取系统 时间就不需要查询表,那么其执行计划显示的type就是Null。 我们对SQL语句的优化,需要达到 range 级别以上。 possible_keys字段: 这个是指查询中可能会用到的索引,不必特别关注。 key字段: 指实际查询中使用的索引,这个也不必特别关注。但是如果这个字段的值是Null,说明该查 询没有用到索引。一般来说,组合索引使用时如果不符合最左匹配,会出现“possible_keys"有 值,但”key"字段为空的情况。 Extra字段: extra这个单词的意思是额外的。这里这个字段表示针对该查询干了哪些额外的事情,也是 一个比较重要的字段。关于这个字段的值,常用的是以下几个: 1,Using filesort。如果没有使用索引排序,而是使用了额外的排序规则,那么Extra就会 记录Using filesort。 2,Using temporary。tempoary的意思是临时工,它表示Mysql使用了临时表存储中间结 果。一般而言【 group by,distinct】之类的语句需要用到“临时工”。 3,Using index。它表示使用了覆盖索引。命中覆盖索引则不需要回表。另外,之前介绍索 引条件下推时也说到,索引条件下推时,extra字段的值也会是Using index。 4,Using where。它表示存储引擎返回数据后,server层使用where条件进行了数据筛选。 5,Select tables optimized away。这个表示没走到查询阶段就可以返回结果了。按官网 的说法,“在没有GROUP BY子句的情况下,基于索引优化MIN/MAX操作,或者对于MyISAM 存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成 优化。” 拿到数据后,最后一步就是返回数据给客户端了。Mysql采用的是一种即时返回的策略。它 不会等所有结果集就位后一起返回,而是从产生第一条结果时就开始返回数据到客户端。当然, 如果配置了缓存,结果会放到缓存里。返回数据是走网络,所以一个显而易见的结论是:传输的 数据越少返回越快。所以提取数据时,应该指明需要的字段,而不是无脑写“ * ”。 索引结构与调优 Mysql采用经过改良的B+树来存储索引数据。 B+树是一个加强版的B树,Mysql的B+树 与传统的B+树还不太一样,它有以下特征: 1,关键字个数等于路数。 2,InnoDB里,B+树节点不存储实际数据,所有数据放到最底层的叶子节点里。这些叶子 节点投影成了一个有序数组。 3,InnoDB里,每个叶子节点维护了一个指向它下一个叶子节点的指针,这样就形成了一个 双向链表。由于B树的叶子节点投影本身有序,这就成了一个有序的双向链表。这个有序链表对 范围查找非常友善,不需要再回到根节点重复IO。 4,这棵树的分叉非常多,使得这棵树的结构非常扁平。这样的结构能够在存储尽可能多的 树的同时,让IO次数尽可能少。假设这棵树只有三层,按每条数据1KB计算,大约有130万子节 点,能存储两千多万条数据。 5,存储数据时,需要进行子节点的分裂和合并重新调整。 整体结构如下: 根据以上结构,能推导出以下优化经验: 1,仅在需要的时候创建索引。索引再能满足需求的前提下,越少越好。一方面索引本身也 会占据不少的数据库资源(磁盘+内存),另一方面,在对数据进行增删改时,需要同步更新索 引树导致额外开销。 2,索引的字段要保证比较高的离散度。这是因为,如果一个字段的离散度太低,那么扫描 这颗B+树时就需要走更多的路数。当离散度低到一定程度时,甚至还不如直接走叶子节点扫全 表,这样至少能适用磁盘顺序读,比走索引更快。 3,创建联合需要符合最左匹配原则。对于联合索引,B+树上的键值也是从左往右构建和匹 配索引数据的。如果左边第一个命中,则直接按第一个走。未命中会继续走第二个,以此类推。 4,应用覆盖索引减少回表操作。根据上面的B+树结构可知,聚簇索引的叶子节点存储原始 数据。所以如果SQL的返回值刚好是索引值,那就不需要走叶子节点取数,直接在索引层就能返 回了。 5,有序ID减少页分裂。由于这棵B+树要保证叶子节点有序,所以如果插入了无序的ID,会 重新排列叶子节点。但如果数据本身有序,那么只需要顺序往后写即可,会更加高效。 6,查询条件里尽量不要有函数运算或子查询。索引树放在存储引擎层,只能做简单的逻辑 判断。如果写SQL时能保证逻辑简单,那这部分逻辑可以下推到存储引擎层,从而只返回符合条 件的数据。否则,只能用尽可能简单的方式扫描尽可能多的数据,再由执行引擎过滤掉不符合要 求的数据,导致性能低下。 SQL优化经验 其实,掌握了SQL的分析方法,优化方法也就出来了。有很多零零碎碎的优化经验,这里简 单列一些: 1,尽量避免在WHERE子句中使用函数,因为会使索引失效。 2,使用JOIN语句而不是子查询,因为JOIN语句更有效率。 3,确保表有适当的索引,以加快查询速度。 4,避免在SELECT语句中使用“*”,尽可能只选择需要的列。 5,尽量避免使用OR操作符,它会导致全表扫描而不是使用索引。 6,对于大型数据集,请考虑分区表或使用分页技术。 7,使用EXPLAIN语句来检查查询执行计划,找到问题并进行优化。 8,使用合适的数据类型,例如使用INT代替VARCHAR存储数字类型。 9,将多个单行查询组合成一个查询,以减少与数据库服务器的通信次数。 10,定期清理不再使用的索引和表,以保持数据库性能稳定。 个人经验和网传资料太过片面,想要系统掌握SQL优化的方法,还是建议看官网。以下是官 方提供的优化办法: https://dev.mysql.com/doc/refman/5.7/en/optimizing-innodb-loggin g.html Mysql使用基于开销的优化器,大多数时候它能够很好工作。如果查询优化器没有按预期的 方式工作,可以使用 FORCE INDEX 扫描表告诉 MySQL强制走索引。与使用给定索引相比,表 扫描非常昂贵: 1 SELECT * FROM t1, t2 FORCE INDEX (index_for_column) 2 WHERE t1.col_name=t2.col_name;


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部