sql优化的记录
写在前面:
最近看了多个多的mysql的文章,视频也看了一小部分高级mysql这本书,做一些小记录分享一下。另外也是避免忘记。Mysql逻辑架构图
约会网上一张图
Mysql主要核心
都都在中间阶段层,包括了查询解析,分析,优化,缓存,内置函数(时间,数学)。
所有跨存储引擎功能也都在这一层实现。
最下层为存储引擎,负责Mysql的数据存储和提取。Mysql查询过程
如上这张图也没完全理解明白,只看图也能捏个大概,放在这里希望各位看官脑袋里能有一张这个图的概念。
我们总是希望MySQL能够获得更高的查询性能,最好的方法是弄清楚MySQL是如何优化和执行查询的。一旦理解了这一点,就会发现:很多的查询优化工作实际上就是遵循一些原则让MySQL的优化器能够按照预想的合理方式运行而已。关于查询缓存
在执行一个查询语句前,如果查询缓存是打开的,那么mysql会检查该查询语句是否命中中缓存。如果命中中会直接返回缓存中的结果
(在这种情况下,查询不会被解析,也不会生成执行计划,更不会执行)
3.1
mysql将缓存放在一个引用表中,不要理解为一张表,我的理解就是类似的Hashmap的结构。通过一个哈希值索引,这个哈希值通过查询本身,当前要查询的数据库。。等各种条件计算得来(既然是hash那么如果查询中有不确定函数,偏差一个标点符号都会导致不命中)
3.2
是缓存就有可能失效,MySQL的查询缓存系统会跟踪查询中涉及的每个表,如果这些表(数据或结构)发生变化,那么和这张表相关的所有缓存数据都将重复。正因为如此,在任何的写操作时,
3.3
给我的感觉Mysql查询缓存对于于写密集应用不适用
做一个扩展,我还没试过(query_cache_type设置为EMAND,这时只有加入SQL_CACHE的查询才会走缓存,其他查询则不会)
(看累了,可以先养养眼)
4)性能优化记录
这里总结几个可能容易理解错误的技巧:
1)通常来说把可为NULL的列替换为NOT NULL不会对性能提升有什么帮助,只是如果计划在列上创建索引,就应该将列设置为NOT NULL。
2)对整体类型指定宽度,尺寸INT(11),没有任何卵用。INT使用32位(4个字节)存储空间,那么它的表示范围已经确定,所以INT(1)和INT(20)对于存储和计算是相同的。
3)UNSIGNED表示永久负值,大致可以使正数的上限提高一倍。有时TINYINT存储范围是-128〜127,而UNSIGNED TINYINT存储的范围却是0-255。
4)TIMESTAMP使用4个字节存储空间,DATETIME使用8个字节存储空间。由此,TIMESTAMP只能表示1970-2038年,比DATETIME表示的范围小,而且TIMESTAMP的值因时区不同而不同。
5)大多数情况下没有使用枚举类型的必要,其中一个缺点是枚举的字符串列表是固定的,添加和删除字符串(枚举选项)必须使用ALTER TABLE(如果只在列表末尾追加元素,不需要重建表)。
6)大表ALTER TABLE非常耗时,MySQL执行大部分修改表结果操作的法是用新的结构创建一个张空表,从旧表中插入所有的数据插入新表,然后再删除旧表。尤其当内存不足而表又很大,而且还有很大索引的情况下,耗时更久。当然有一些奇技淫巧可以解决这个问题,有兴趣可自行查阅。
关于SqL优化的方法,网上太多了掘金知乎都有很多反正我的建议就是多用用EXPLAIN吧〜
下面我想记录一些一些sql优化技巧中,为什么要这样优化
首先我提两个概念
1.回表
2.覆盖索引
innodb存储引擎中,必须需要一个主键,如果你不定义主键
那么第一个为非null的唯一列就是主键索引,否则会创建一个隐藏的row_id作为主键索引。
主键索引很快,直接定位记录行(Innodb的主键索引的叶子副本存储行记录)但innodb的普通索引叶子数组存储的就是主键值
举个列子如果一个表是这样的
t(id PK,名称KEY,性别);
id是主键,名称是普通索引
下面是SQL语句SELECT * from users WHERE name = "张志明"
这个语句执行两次操作,先通过名称的索引树找到叶子计数器“张志明”存储的主键值
再通过主键值找到*所有的记录值
这个就是回表!
接下来说说覆盖索引,在说这个概念之前我看了官网
是这么说的也就是如果可以在一棵索引树上就能找到的记录就可以走覆盖索引,也就是使用explain看得到
使用index
这样就可以解决回表,通过一次查询即可找到记录,所以索引的设计也是很有学问的
关于最左匹配原则
我在查阅的资料中看到了这么两个图片,包括我掘金和知乎中也看到过
从这种图可以抛光,多列索引,首先是根据第一个细分(最左边的排序)然后再根据第二列排序,以此类推所以只有当第一列为等值的时候,才可以找到第二列排好序的数据这就是为什么是最左原则
为什么不要使用SELECT *
1)MySQL客户端/服务端通信协议是“半双工”的:在任一时刻,或者是服务器向客户端发送数据,或者是客户端向服务器发送数据,这两个动作不能同时发生。一次一端开始发送消息,另一端要接收完整个消息才能响应它,
所以我们无法也无须将一个消息切成小块独立发送,也没有办法进行流量控制。
2)客户端使用一个单独的数据包将查询请求发送给服务器,所以当查询语句很长的时候,需要设置max_allowed_packet参数。但是需要注意的是,如果查询实在是很大,服务端会拒绝接收更多数据并引发异常。
3)与之相反的是,服务器响应给用户的数据通常会很多,由多个数据包组成。但是当服务器响应客户端请求时,客户端必须完整的接收整个返回结果,而不能简单的只取前面几条结果,然后让服务器停止发送。
实际上在实际开发中,尽量保持查询简单且仅返回必要的数据,重组通信间数据包的大小和数量是一个非常好的习惯,这也是查询中试图避免使用SELECT *以及附加限制条件的原因之一。还有一个原因就是,使用*能使用到覆盖索引的地方也用不到了,便会进行一次回表操作。
总结:
暂时就说这么多了,还需多多学习,多看看Mysql文档和书共勉