MySQL在大数据、高并发场景下的SQL语句优化和"最佳实践"

时间:2022-10-31 08:46:48

本文主要针对中小型应用或网站,重点探讨日常程序开发中SQL语句的优化问题,所谓“大数据”、“高并发”仅针对中小型应用而言,专业的数据库运维大神请无视。以下实践为个人在实际开发工作中,针对相对“大数据”和相对“高并发”场景的一些应对策略,部分措施并没有经过严格的对比测试和原理分析,如有错漏欢迎各种批评指教。减少查询的影响结果集,避免出现全表扫描。影响结果集是SQL优化的核心。影响结果集不是查询返回的

本文主要针对中小型应用或网站,重点探讨日常程序开发中SQL语句的优化问题,所谓“大数据”、“高并发”仅针对中小型应用而言,专业的数据库运维大神请无视。以下实践为个人在实际开发工作中,针对相对“大数据”和相对“高并发”场景的一些应对策略,部分措施并没有经过严格的对比测试和原理分析,如有错漏欢迎各种批评指教。

减少查询的影响结果集,避免出现全表扫描。

影响结果集是SQL优化的核心。影响结果集不是查询返回的记录数,而是查询所扫描的结果数。通过Explain或Desc分析SQL,rows列的值即为影响结果集(还可以通过慢查询日志的Rows_examined后面的数字得到)。

以下是我常用的一些SQL优化策略:

去掉不必要的查询和搜索。其实在项目的实际应用中,很多查询条件是可有可无的,能从源头上避免的多余功能尽量砍掉,这是最简单粗暴的解决方案。

合理使用索引和复合索引。建索引是SQL优化中最有效的手段。查找、删除、更新以及排序时常用的字段可以适当建立索引。不过要注意,单条查询不能同时使用多个索引,只能使用一个索引。查询条件较多时,可以使用多个字段合并的复合索引。切记,使用复合索引时,查询条件的字段顺序需要与复合索引的字段顺序保持一致。

谨慎使用not in等可能无法使用索引的条件。索引也不是什么时候都可以发挥作用的,当出现"not in","!=","like '%xx%'","is null"等条件时,索引是无效的。使用这些条件的时候,请放到能有效使用索引的条件的右边。设计表结构时,个人建议尽可能用int类型代替varchar类型,int类型部分时候可以通过大于或小于代替"!="等条件,同时也方便满足一些需要按类型排序的需求,至于可读性的问题,完善好数据库设计文档才是明智的选择。同时建议把所有可能的字段设置为"not null",并设置默认值,避免在where字句中出现"is null"的判断。

不要在where子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将无法正确使用索引。尽可能少用MySQL的函数,类似Now()完全可以通过程序实现并赋值,部分函数也可以通过适当的建立冗余字段来间接替代。

在where条件中使用or,可能导致索引无效。可用 "union all" 或者 "union" (会过滤重复数据,效率比前者低) 代替,或程序上直接分开两次获取数据再合并,确保索引的有效利用。

不使用select * ,倒不是能提高查询效率,主要是减少输出的数据量,提高传输速度。

避免类型转换,这里所说的“类型转换”是指where子句中出现字段的类型和传入的参数类型不一致的时候发生的类型转换。

分页查询的优化。页数比较多的情况下,如limit 10000,10 影响的结果集是10010行,查询速度会比较慢。推荐的解决方案是:先只查询主键select id from table where .. order by .. limit 10000,10(搜索条件和排序请建立索引),再通过主键去获取数据。

统计相关的查询。影响结果集往往巨大,且部分SQL语句本身已经难以优化。因此,应避免在业务高峰期执行统计相关的查询,或者仅在从库中执行统计查询。部分统计数据,可以通过冗余的数据结构保存,同时建议把数据先保存在内存、缓存中(如redis),再按一定策略写入数据库。

不使用任何连表查询,通过分库和分表实现负载均衡。

随着数据量的增加,连表操作往往会导致影响结果集大增,从SQL优化的层面已经解决不了问题了。

此时,分库和分表是解决数据库性能压力的最优选择(具体分库和分表的方案通常结合实际业务的应用场景来确定,此处略过)。这里重点谈,如何更好的实现或者过渡到分库、分表的分布式数据库架构。

核心点就是必须先去除数据表之间的关联,即不用外键,不使用任何连表查询。为了确保不进行连表操作,在设计数据库表结构的时候,就需要设计适度冗余的字段来达到不连表的目的。

对于一些操作日志、支付记录等,设计一些记录用户信息的字段,个人认为其实不能算冗余,毕竟用户信息往往会更改,但是这种类似操作日志的表确实是需要记录用户操作时的信息,并且不需要在用户更新信息时同步更新。

实际开发中,为了实现不进行连表而冗余的字段,往往是需要在原表更新数据的时候同步更新冗余字段的数据的,如果应用层没有对数据表操作做合理封装,这往往是个棘手的问题,也不方便维护。

当然,现在主流的应用框架,一般采用orm的方式处理数据表,所以问题不大。相反,不连表事实上还可以提高开发效率,比如通过用户ID获取用户姓名操作,如果不连表就可以确保各个业务模块都通过同样的方式去获取用户姓名,调用同一个封装好的方法,这样,就能很方便的统一在应用层加入缓存机制或添加统一的业务逻辑。

同时如果要对用户表进行分库分表,通过应用层程序就可以简单平滑的实现。

使用Innodb。

关于Innodb和Myisam对比,我就不多说了。Myisam的表级锁是致命问题,考虑到MySQL已经默认使用Innodb作为数据库引擎,个人建议大部分情况可以直接使用Innodb,其他引擎这里就不详细讨论了。

使用缓存。

1) 尽可能在程序上实现常用数据的缓存,目前主流的应用框架应该都能快速实现缓存的需求。如果在程序上没有实现数据缓存,开启数据库的query cache也是缓解数据库压力的方式之一,如果确认使用,记得定时清理碎片flush query cache。

服务器相关优化

MySQL服务配置以及分布式架构的实现,请根据实际应用场景和业务需求定制,非本文重点,不做深入探讨。

以上内容希望帮助到大家,更多相关知识请关注我的专栏PHP​zhuanlan.zhihu.com

MySQL在大数据、高并发场景下的SQL语句优化和"最佳实践"的更多相关文章

  1. 【转】记录PHP、MySQL在高并发场景下产生的一次事故

    看了一篇网友日志,感觉工作中值得借鉴,原文如下: 事故描述 在一次项目中,上线了一新功能之后,陆陆续续的有客服向我们反应,有用户的个别道具数量高达42亿,但是当时一直没有到证据表示这是,确实存在,并且 ...

  2. HttpClient在高并发场景下的优化实战

    在项目中使用HttpClient可能是很普遍,尤其在当下微服务大火形势下,如果服务之间是http调用就少不了跟http客户端找交道.由于项目用户规模不同以及应用场景不同,很多时候可能不需要特别处理也. ...

  3. C++高并发场景下读多写少的解决方案

    C++高并发场景下读多写少的解决方案 概述 一谈到高并发的解决方案,往往能想到模块水平拆分.数据库读写分离.分库分表,加缓存.加mq等,这些都是从系统架构上解决.单模块作为系统的组成单元,其性能好坏也 ...

  4. Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%。再往后,每提高0.1%,优化难度成指数级增长了。哪怕是千分之一,也直接影响用户体验,影响每天上万张机票的销售额。 在高并发场景下,提供了保证线程安全的对象、方法。比如经典的ConcurrentHashMap,它比起HashMap,有更小粒度的锁,并发读写性能更好。线程安全的StringBuilder取代S

    Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%.再往后,每提高0.1%,优化难度成指数级增长了.哪怕是千分之一,也直接影响用户体验,影响每天上万张机 ...

  5. 高并发场景下System.currentTimeMillis()的性能问题的优化 以及SnowFlakeIdWorker高性能ID生成器

    package xxx; import java.sql.Timestamp; import java.util.concurrent.*; import java.util.concurrent.a ...

  6. 高并发场景下System.currentTimeMillis()的性能问题的优化

    高并发场景下System.currentTimeMillis()的性能问题的优化 package cn.ucaner.alpaca.common.util.key; import java.sql.T ...

  7. 高并发场景下System.currentTimeMillis()的性能优化

    一.前言 System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我也不知道,不过听说在100倍左右),然而该方法又是一个常用方法, 有时不得不使用, ...

  8. Java高并发情况下的锁机制优化

    本文主要讲并行优化的几种方式, 其结构如下: 锁优化 减少锁的持有时间 例如避免给整个方法加锁 1 public synchronized void syncMethod(){ 2 othercode ...

  9. 大数据高并发系统架构实战方案(LVS负载均衡、Nginx、共享存储、海量数据、队列缓存)

    课程简介: 随着互联网的发展,高并发.大数据量的网站要求越来越高.而这些高要求都是基础的技术和细节组合而成的.本课程就从实际案例出发给大家原景重现高并发架构常用技术点及详细演练. 通过该课程的学习,普 ...

随机推荐

  1. dede 数据库类使用列表

    dedecms的数据库操作类,非常实用,在二次开发中尤其重要,这个数据库操作类说明算是奉献给大家的小礼物了. 引入common.inc.php文件 require_once (dirname(__FI ...

  2. float 保留两位小数

    1.页面运算格式化数字 页面上有时候会用到数字的运算,运算过后会出现1.5999999999999这么长的数字,需要格式化数字,比如保留两位有效数字 首先导入这个标签 <%@ taglib ur ...

  3. 【滚动数组】【状压dp】Gym - 100956F - Colored Path

    f(i,j,S)表示到(i,j),且经由的路径上的颜色集合为S的价值的最小值,从上方和左方转移过来即可. 要注意,内存不足,需要滚动数组优化,即使用了map,还是需要. 路径输出的时候,可以再跑一遍d ...

  4. C&num;多态及接口

    直接看代码吧 using System; using static System.Console; namespace ConsoleApp { //使用abstract,抽象类或方法,不能使用vir ...

  5. Spring Boot &plus; JPA 因为 javassist 包出现 NullPointerException 问题的解决

    Caused by: org.hibernate.MappingException: Could not get constructor for org.hibernate.persister.ent ...

  6. 关于Promise的记录和理解

    在JavaScript中,所有的代码都是单线程执行的,这就导致了其所有的网络请求,IO操作,浏览器时间等都是异步非阻塞的模式执行的,这就使得代码的执行顺序可能会超出我们的掌控. 尤其是当多个异步操作待 ...

  7. BZOJ4755 &lbrack;JSOI2016&rsqb;扭动的回文串 【后缀数组】【manacher】

    题目分析: 我写了史上最丑的后缀数组,怎么办? 首先manacher一遍两个串,这样只用考虑第三问.用$作为间隔符拼接两个串,把第一个串翻转.枚举回文中心,取最长的回文串,对于剩下的部分利用LCP匹配 ...

  8. 【转】完整精确导入Kernel与Uboot参与编译了的代码到Source Insight&comma;Understand&comma; SlickEdit

    The linux kernel and u-boot contains lots of files, when we want to broswe the source code,we just w ...

  9. Oracle数据库常用Sql语句大全

    一,数据控制语句 (DML) 部分 1.INSERT  (往数据表里插入记录的语句) INSERT INTO 表名(字段名1, 字段名2, ……) VALUES ( 值1, 值2, ……); INSE ...

  10. HTML:Hyper Text Markup Language 超文本标记语言

    1.HTML是什么? *Hyper Text Markup Language 超文本标记语言 *Hyper Text:超链接.把不同空间的资源,整合在一起,形成逻辑上的网状结构. *Markup La ...