当前位置:网站首页>mysql优化sql相关(持续补充)
mysql优化sql相关(持续补充)
2022-07-27 05:12:00 【铁柱同学】
一、前言
此处为博主在开发中遇到的优化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速度的原因:mysql的 io 以page为单位,因此不必要的数据(大字段)也会随着需要操作的数据一同被读取到内存中来,这样带来的问题由于大字段会占用较大的内存(相比其他小字段),使得内存利用率较差,造成更多的随机读取。从上面的分析来看,我们已经看到性能的瓶颈在于由于大字段存放在数据页中,造成了内存利用较差,带来过多的随机读。
记录这条的目的是网上很多人的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_at,updated_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
边栏推荐
- DDD领域驱动设计笔记
- How to judge whether an object is empty in JS
- Web3 traffic aggregation platform starfish OS interprets the "p2e" ecosystem of real business
- MySQL快速比较数据库表数据
- 刷脸支付用户主要优势是智能化程度高
- How to judge whether a property belongs to an instance object or inherits from a constructor in JS
- What are Dom and BOM in JS
- Do you really know session and cookies?
- 你真的了解 Session 和 Cookie 吗?
- 软件测试常见面试题
猜你喜欢

Do you really know session and cookies?

GameFi如何破圈,AQUANEE靠真正“P2E”展现风采

Personal collection code cannot be used for business collection

Getaverse, a distant bridge to Web3

一文读懂Elephant Swap的LaaS方案的优势之处

Minio8.x version setting policy bucket policy

M-DAO 7大赋能方案,助力DAO生态走向模式与标准化

Ubuntu:安装PostgreSQL

SeekTiger即将上线STI聚变Mining功能,获取OKA通证

Minimum handling charges and margins for futures companies
随机推荐
The written test questions of 25 large Internet companies are summarized, and I have encountered packages.
Minimum handling charges and margins for futures companies
graph-node部署
什么是Alpha和Beta测试?
你真的了解 Session 和 Cookie 吗?
万字解析MySQL索引原理——InnoDB索引结构与读取
How can seektiger go against the cold air in the market?
期货开户怎么选择好的期货公司 ?
Getaverse, a distant bridge to Web3
Move protocol launched a beta version, and you can "0" participate in p2e
Common interview questions in software testing
市场冷空气来袭,SeekTiger如何逆流而上?
The difference between deep copy and shallow copy in JS
Differences among apply, call, and bind in JS
Seektiger's okaleido has a big move. Will the STI of ecological pass break out?
What are the traversal methods of arrays in JS
根据文本自动生成UML时序图(draw.io格式)
Mysql5.7版本如何实现主从同步
期货公司最低标准的手续费和保证金
jenkins构建镜像自动化部署