SQL优化学习

一、学习框架

mysql常见问题

image
  1. 一条查询语句的执行过程:一条语句在客户端执行,通过连接器跟服务器建立起连接,通过连接器控制连接的数量,获知客户端的状态,为每一个客户端建立起TCP连接。然后到词法分析器,进行词法分析和语法分析,词法分析将语句解析成一个个token词,语法分析将token词生成语法树。优化器负责生成执行计划,选择索引。根据优化器选择的索引,执行器调用存储引擎的接口取数据,执行语句。

重点:优化器如何选择索引?

  • 扫描行数:扫描行数怎么判断?一个索引上不同的值的个数,我们称之为“基数”(cardinality)。也就是说,这个基数越大,索引的区分度越好 。MySQL是通过采样统计的方式来判断的。但是扫描行数只是个预估值,存在对mysql误导的情况
  • 是否使用临时表 --见下方关于排序的讲解
  • 是否需要排序
  1. 索引:索引需要知道索引的结构,聚集索引和非聚集索引的区别,普通索引和唯一索引的区别,什么时候适合用普通索引,普通索引能够用上change buffer的优化

  2. 覆盖索引&回表:如果需要查询的字段,都包含在索引里面(通常是联合索引),那么就不需要去主键索引回表,因为只有主键索引的叶子结点包含row数据行,这也是一种常见的优化手段。
    需要注意的是回表扫描的行数不会体现在rows_examed里,因为rows_examed是表示执行引擎从存储器取了多少次数据,而是否回表是取决于不同的存储引擎,例如innoDb需要回表,myslam就不需要回表
    如果有联合主键索引(a,b),和索引c,a,c,ac,abc都可以走索引

  3. 索引下推:索引下推的前提是有覆盖索引,通过索引下推,在覆盖索引遍历的时候将一些不符合的行过滤掉,减少回表的次数。

无索引下推:

image

有索引下推:

image
  1. 排序---总结,如果排序字段有索引,能够自然有序,
  • rowid排序
  • 全字段排序
  • 内排序
  • 外排序
  1. 慢查询
  • 开启:slow_query_log 慢查询开启状态,long_query_time 查询超过多少秒才记录
  • 参数

在有些场景下,执行器调用一次,在引擎内部则扫描了多行,因此引擎扫描行数跟 rows_examined 并不是完全相同的

image
  1. explain语句
  • 通常看 possible_keys 以及 key 两个字段

  • possible_keys 的内容取决于 where 语句 + EXPLAIN TABLE 的结果,即 where 会用到哪些 EXPLAIN TABLE 中能看到的索引

  • key 表示 MySQL 存储引擎实际选择的索引。这个索引可能不是 possible_keys 中列出来的

  • key_len 表示实际使用的索引的长度,通常能根据这个字段 + key 来判断用了 key 的哪几个字段

当前表的关联方式,也对查询性能影响较大。对应的在 EXPLAIN 结果中,有个 type 字段来参考关联方式。通常问题 SQL 中会出现 type=ALL 或者 type=index 的情况,即全表扫描。下面列举了下 type 的可能值,按照性能从好到差来排序。

  • system & const: 表示你条件可以被转换为常数列(即能通过条件确定唯一值),比如

  • 只有一行记录的系统表:system

  • where 条件是 pk or unique key 的唯一值,例如 a=1 & a is pk or uk

  • eq_ref & ref & ref_or_null

  • eq_ref:previous table 的内容能在当前 table 唯一确认一列

  • ref:用了索引,但是做不到 eq_ref 那么好,就是 ref

  • ref_or_null:等价 ref,比 ref 多比较了一个 null 的情况

  • fulltext:使用了 FULLTEXT 索引

    • index_merge
  • 采用了多个索引,此时 key 列会展示用到的具体索引

  • unique_subquery & index_subquery

  • 针对 IN 类型的 subquery 优化

  • range

  • 用了索引,判断采用范围读取的方式比较好

  • 通常会出现在范围类型的 operator & 当前索引中用到的列,其区分度比较小的情况

  • range 对 column 相关 operator 是有要求的,支持的有:=, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, or IN()

  • index & all

  • 都是全表扫描,区别是用非主键还是主键

  • 要注意的是,不同索引的扫描,数据的返回顺序是不一样的

  • rows:预估扫描行

二、排查思路

  1. 索引使用不符合预期
  • 没有用索引---选择合适的索引

  • 优化器选择了错误索引---优化器为什么会选错索引呢?force_index或者使用order by 引导优化器使用合适的索引

  • 索引没有实现覆盖索引---根据业务决定如何用上覆盖索引的优化

  1. 扫描行数过多---limit m,n 跳过m行取n行,会出现扫描m+n的结果
  2. IO存在问题,获取不到连接,或者被无效的连接占有
  3. 返回不需要的列,慎用select * 尤其是一些字段是json,如果使用select *会造成IO压力

三、分析慢查询的思路

  1. explain查看情况
  2. 分析语句的执行计划和执行过程
  3. 结合业务寻找优化点

SQL优化---while true 并且limit m,n导致的SQL扫描行数过多。limit m,n会取出m+n行数据,然后跳过m行,因此rows_examed会是m+n。

image
SELECT id, uid2, accepted, frm, crtime, uptime FROM contact_request WHERE (uid=110977129) AND (type=1) AND (frm!='addrbook_multi') AND (crtime>'2000-01-01 00:00:00') ORDER BY crtime desc LIMIT 0,1001

def get_exclude_talent_ids_add_fr(manager, uid, start):
    to_uids = []
    ht = {}
    page = 0
    size = 1000
    while True:
        cr_rows = network_service.get_cttreq_all_addfr_requests(manager=manager, uid=uid, crtime=start,
                                                                page=page * size, count=size + 1)
        remain = 0
        if cr_rows:
            if len(cr_rows) > size:
                remain = 1
            for cr_row in cr_rows:
                to_uid = cr_row['uid2']
                if to_uid not in ht:
                    to_uids.append(to_uid)
                    ht[to_uid] = to_uid
        if remain:
            page += 1
        else:
            break
    return to_uids

Limit m,n-->id>max_fid limit n

explain SELECT count(*)  FROM contact_request WHERE (uid=110977129) AND (type=1) AND (frm!='addrbook_multi') AND (crtime>'2000-01-01 00:00:00') and id>max_id ORDER BY crtime desc LIMIT 1001

def get_exclude_talent_ids_add_fr_new(manager, uid, start):
    to_uids = []
    ht = {}
    size = 1000
    max_id = 0
    while True:
        cr_rows = network_service.get_cttreq_addfr_requests_by_crtime(manager=manager, uid=uid, crtime=start, limit=size+1, id=max_id)
        remain = 1 if len(cr_rows) > size else 0
        if cr_rows:
            start = cr_rows[len(cr_rows) - 1]['crtime']-datetime.timedelta(seconds=1)
            max_id = cr_rows[len(cr_rows) - 1]['id']
            for cr_row in cr_rows:
                to_uid = cr_row['uid2']
                if to_uid not in ht:
                    to_uids.append(to_uid)
                    ht[to_uid] = to_uid
        if not remain:
            break
    return to_uids

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容