mysql优化sql相关(持续补充)

一、前言

      此处为博主在开发中遇到的优化mysql 的笔记,记录下来也是为了方便自己查阅,在每次写完或者要写sql之前都看一看,仅作记录。以下也都是博主在开发中遇到并优化的过程,踩坑不计其数,也许只有这样才能变得强大吧。

二、正文

1、不要对索引的字段进行函数操作,例如:

				$start_time是时间戳格式
				log_time是格式化后的日期格式
			select xxx from xx where 1 and unix_timestamp(log_time) between  {$start_time})  and {$lt_end_time}  and drr_weekly  = 0
				这里直接转换log_time字段的格式,虽然sql短了,但是实际上并没有用到log_time索引,sql执行速度缓慢
			修改后:
				用到了log_time索引,sql执行速度明显上升
			select xxx from xx where 1 and log_time between  from_unixtime({$start_time}) and from_unixtime({$lt_end_time}) and drr_weekly  = 0

      第一个sql因为对索引字段进行了函数操作,所以并没有用到索引,此处需要谨记。如果对数据库不熟悉,那么请先查看数据库的结构,了解哪个字段有索引,这样写sql的时候才能写的更精准。

2、 所有的where条件查询的时候,都要确保查询的where字段不为空,要有这个判断的步骤,不然sql会报错。其次是要尽可能的处理数据库中的字段,因为有的时候数据库存储的字段可能不规范,所以要尽量处理这些不规范的字段,保证程序的稳定性

3、 mysql查询的where条件是大小写不敏感的,如果需要在查询的时候区分大小写,那么可以加上额外的一些参数:
参考:https://www.cnblogs.com/yeahwell/p/6904310.html

4、 新建数据表的时候,对于一些需要联表的字段,比如user_id之类的,一定要加上索引。还有会经常出现到where条件中的字段,也要加上索引,这样提前加好索引,能省很多事情

5、 get和post拿到的值, 绝对不能直接放在sql里,参数需要转义, 不然sql会出错, 而且会被sql注入,如果确定类型的,需要格式化一下,比如是int的, 直接intval()一下,字符串的话,要使用转义函数addslashes()转义一下,还需要判断是否为空, 空的话, 就没有必要查询sql了

关于字符串的转义参考:https://blog.csdn.net/LJFPHP/article/details/86549582

6、 数据库建表的时候,要先看一下项目已存在的表是什么编码格式,然后保持编码格式一致。
比如目前数据库的编码格式都是utf8(utf8_general_ci),如果我们在建表的时候选择字段或者数据表的编码格式是(utf8_unicode_ci),那么在mysql 5.7的严格模式下,插入就会报错:1267 Illegal mix of collations (utf8_general_ci,IMPLICIT) and (utf8_unicode_ci,IMPLICIT) for operation

修改数据表的编码格式:alter table `table_name` convert to character set utf8

7、 每次写完功能之后,要对复杂的sql进行explain查看,明确索引有没有用到,extra里面提示信息是什么,是否需要优化,一般来说索引能用到,type能达到range以上就可以,如果Extra里面含有 Using filesort或者 Using temporary 则代表该sql需要进行优化。

关于explain,可以参考博客:https://www.cnblogs.com/yycc/p/7338894.html

============================ 2019年5月10日13时更新 ==========================

8、 确定表中字段的类型,和get/post传过来的参数类型。在操作sql的时候,字段类型和参数类型要保持一致,保持不一致则不能用到索引。比如我这边是字符串的字段,version = 2.0,死活用不到索引,当改为:version = '2.0'的时候,类型一致,就成功用到了索引。

9、 此处接上面第七条
有时候我们本地的数据库数据很少,用explain测试毫无意义,因为mysql在数据量小的时候,是不会选择使用索引的,只有当数据量达到一定程度才会选择使用索引。所以我们在测试之前可以先往数据库循环插入一些数据,方便测试。

		测试插入数据(循环插入一万条,方便查询)
		 /*   for($i=0;$i<10000;$i++)
		 {
   				 $sql = "INSERT INTO `xxxx` ( `buyer_id`, `xxxx`, `xxxx`, `xxxxx`, `transaction_id`, `xxxx`, `pay_money`, `order_money`, `xxxx`, `pay_date`, `remark`, `order_ip`, `xxxx`, `transaction_type`, `server_unique_flag`, `order_time`, `pay_time`) VALUES
			 ( 1, 6, 6, '2219F75724AE60AC', '7A1E0AA39766D615', 1, 99.9000, 99.9000, '2019-03-24 23:35:08', '2019-03-25 23:35:08', '第一条记录', '192.168.1.223', 'cn', 404, '1', 1553002641, 1553002741)";
   				 $data =  G_FUNCTION::getProjectDb("slave")->createCommand($sql)->execute();
			}*/

10、 表设计时需要考虑到这样的问题。像这种存放大数据的字段需要拆分到其他 表中。或者是考虑细化存储其中的数据。因为innodb引擎中。对于大字段如text blob等,只会存放768个字节在数据页中,而剩余的数据会存储在溢出段中,在查询这些大字段的时候,会去访问很多页,从而影响sql的性能。 最大768字节的作用是便于创建前缀索引/prefix index,其余更多的内容存储在额外的page里,哪怕只是多了一个字节。因此,所有列长度越短越好(能用 varchar 就别用text)。

看这个博客了解原理:http://www.cnblogs.com/chenpingzhao/p/6719258.html

影响sql速度的原因: mysqliopage为单位,因此不必要的数据(大字段)也会随着需要操作的数据一同被读取到内存中来,这样带来的问题由于大字段会占用较大的内存(相比其他小字段),使得内存利用率较差,造成更多的随机读取。从上面的分析来看,我们已经看到性能的瓶颈在于由于大字段存放在数据页中,造成了内存利用较差,带来过多的随机读。

记录这条的目的是网上很多人的sql很慢都是由于这种大字段引起的,所以在建表时进行优化十分必然。

扩展:关于innode存储的page介绍,大家可以参考这篇:https://blog.csdn.net/voidccc/article/details/40077329

11、 新建一个表,表的结构和内容都使用在另一张表中查询出来的数据

create table table2 select * from table1;

12、 InnoDB使用的是聚簇索引,将主键组织到一棵B+树中,而行数据就储存在叶子节点上,若使用"where id = 14"这样的条件查找主键,则按照B+树的检索算法即可查找到对应的叶节点,之后获得行数据。(查找主键是一次索引)
若对Name列进行条件搜索,则需要两个步骤:第一步在辅助索引B+树中检索Name,到达其叶子节点获取对应的主键。第二步使用主键在主索引B+树种再执行一次B+树检索操作,最终到达叶子节点即可获取整行数据。(查找其他字段是遍历两次b+tree

13、 order by要求排序字段必须是唯一?

答案: 是的,如果order by的字段不是唯一的,那么比如排序字段是time,那么当time相同的时候,就可能会出现遗漏数据的情况。所以针对这种情况,我们在写sql的时候一定要注意,可以考虑使用:xxxx order by time desc,id desc;

还要注意:order by time,id desc; 等同于: order by time asc,id desc。 因为排序默认是使用升序。

不明白的话,可以参考博客:https://blog.csdn.net/tsxw24/article/details/44994835

============================ 2019年6月02日20时更新 ==========================

1、 循环里面执行sql的,如果实在不能把sql拿出来,那应该判断循环条件,符合条件的再执行逻辑,不符合的就不执行逻辑,避免无谓的循环。比如你一上来就循环10次处理数据,如果这些数据有些是不需要处理的,那么就白白浪费了性能

2、 对于子查询来说,子查询里面的句子如果不加where条件的话,代表全表扫描,非常耗费性能,所以要把外面的where条件加进去。外面本来的where条件还是要保留的,防止关键的字段可以有重复值的话,会造成数据重复

3、 group by也可以group by多个字段,有时候有奇效。最后要检查sql查询的是否准确,group by的时候,查询的字段如果不是聚合字段,那么这个值是随机给出的,并不是你想要的最大或者做小的值。

4、 单个sql的速度永远比多个sql的速度快。单个sql更方便优化和维护,明确功能之前,先尽量写一个sql来实现复杂的操作。多个sql的话,势必会有循环,对于大数据来说,循环更致命

5、 如果要处理的数据量很大,可以在sql刚开始的地方,就用 limit 0,500来处理,每次查询都保证只处理500条,剩下的用limit 500,500来处理

6、 如果要在where条件中判断字段值是否为null,例如:is_master = null 这种方式是查不出来东西的,必须换成is_master is null这种方式。

7、sql中存在子查询的时候,把order by和limit都加入到子查询,减少子查询的数据量。这样主表和子查询的结果集关联查询的时候,效率会更高。

======================== 2019/12/1=============================
1、 建表的时候,像created_atupdated_at等字段尽量是直接创建,因为随着业务的提升,根据创建时间,修改时间进行一些业务操作是很正常的场景,后续再添加的话,麻烦,而且可能会引起其他的问题 。参考CSDN的博客时间,每次更新,发布时间都会变成当前的,这是为啥呢,因为他们没有记录创建时间,只记录的修改时间。。。

2、sql中存在in(str)查询的时候,需要判断是否存在str的值,如果不进行判断的话,sql就会报错。虽然根据业务逻辑,str为空的概率很小,但是还是要加上这部分判断
3、 善用mysql的函数,比如我们的需求是,当某个字段可能为null时,默认给该字段赋值为1,则使用方式如下:

	coalesce(f.city_level,1)   ,这样当查询到city_level的时候,就正常显示city_level的值,当查询不到的时候,默认为1
	函数解释:coalesce(a,b,c)  函数范围参数abc中第一个非null的值,比如coalesce(1,null,null)返回1, coalesce(null,null,2)返回2

适用环境:左连接或者右连接的时候,匹配不到的行记录为null值,此时我们需要给字段默认值,则可以用到该函数

4、 循环插入,sql拼接的批量插入方式

 $sqlpre = "INSERT IGNORE INTO xxx(xxx,xxx) VALUES ";
    $sqlVal = "";
    $i = 0;
    foreach ($arrDevice as $k => $v) {
        $sqlVal .= "('xxx','xxx'),";
        if ($i >= 500) {
            $sqlVal = substr($sqlVal, 0, -1);
            if (!empty($sqlVal)) {
                $sql = "{$sqlpre} {$sqlVal}";
                Yii::$app->db->createCommand($sql)->execute();  // 500条就插入一次
                $sqlVal = '';
                $i = 0;
            }
            $i++;
        }
    }

这个应用场景就是循环批量插入的时候,我们可以通过计数,每500次插入一次,避免每次只插一条的大开销行为。当然,这里的每次插500条也只是为了举例,如果你要想每次插入很极限的话,参考我的另一篇博客:mysql批量插入数据,一次插入多少行数据效率最高?

======================== 2020/01/20=============================
1、 使用INT UNSIGNED存储IPv4,不要用char(15)
2、 使用varchar(20)存储手机号,不要使用整数
解读:

(1)牵扯到国家代号,可能出现+/-/()等字符,例如+86
(2)手机号不会用来做数学运算
(3)varchar可以模糊查询,例如like ‘138%’

3、 单张表索引数量建议控制在5个以内,互联网高并发业务,太多索引会影响写性能
4、 组合索引字段数不建议超过5

解读: 如果5个字段还不能极大缩小row范围,八成是设计有问题

来自mysql建表军规

每次更新的时候,都要从头看一遍这些优化记录,每次都有新的收货,简直太棒了!

end

评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铁柱同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值