mirror of
https://github.com/Snailclimb/JavaGuide
synced 2025-07-28 12:22:17 +08:00
Compare commits
4 Commits
03970c7ba0
...
2a53550bdc
Author | SHA1 | Date | |
---|---|---|---|
|
2a53550bdc | ||
|
bca0422d4b | ||
|
c2efa48666 | ||
|
5b5f472126 |
32
README.md
32
README.md
@ -158,30 +158,34 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
|||||||
|
|
||||||
## 数据库
|
## 数据库
|
||||||
|
|
||||||
|
### 基础
|
||||||
|
|
||||||
|
- [数据库基础知识总结](docs/database/basis.md)
|
||||||
|
- [字符集详解](docs/database/character-set.md)
|
||||||
|
|
||||||
### MySQL
|
### MySQL
|
||||||
|
|
||||||
**总结:**
|
**总结:**
|
||||||
|
|
||||||
1. [数据库基础知识总结](docs/database/basis.md)
|
- **[MySQL知识点总结](docs/database/mysql/mysql-questions-01.md)** (必看 :+1:)
|
||||||
2. **[MySQL知识点总结](docs/database/mysql/mysql-questions-01.md)** (必看 :+1:)
|
- [一千行 MySQL 学习笔记](docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md)
|
||||||
4. [一千行 MySQL 学习笔记](docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md)
|
- [MySQL 高性能优化规范建议](docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md)
|
||||||
5. [MySQL 高性能优化规范建议](docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md)
|
|
||||||
|
|
||||||
**重要知识点:**
|
**重要知识点:**
|
||||||
|
|
||||||
1. [MySQL数据库索引总结](docs/database/mysql/mysql-index.md)
|
- [MySQL数据库索引总结](docs/database/mysql/mysql-index.md)
|
||||||
2. [事务隔离级别(图文详解)](docs/database/mysql/transaction-isolation-level.md)
|
- [事务隔离级别(图文详解)](docs/database/mysql/transaction-isolation-level.md)
|
||||||
3. [MySQL三大日志(binlog、redo log和undo log)详解](docs/database/mysql/mysql-logs.md)
|
- [MySQL三大日志(binlog、redo log和undo log)详解](docs/database/mysql/mysql-logs.md)
|
||||||
4. [InnoDB存储引擎对MVCC的实现](docs/database/mysql/innodb-implementation-of-mvcc.md)
|
- [InnoDB存储引擎对MVCC的实现](docs/database/mysql/innodb-implementation-of-mvcc.md)
|
||||||
5. [一条 SQL 语句在 MySQL 中如何被执行的?](docs/database/mysql/how-sql-executed-in-mysql.md)
|
- [SQL语句在MySQL中的执行过程](docs/database/mysql/how-sql-executed-in-mysql.md)
|
||||||
6. [字符集详解:为什么不建议在MySQL中使用 utf8 ?](docs/database/character-set.md)
|
- [关于数据库中如何存储时间的一点思考](docs/database/mysql/some-thoughts-on-database-storage-time.md)
|
||||||
7. [关于数据库中如何存储时间的一点思考](docs/database/mysql/some-thoughts-on-database-storage-time.md)
|
- [MySQL中的隐式转换造成的索引失效](docs/database/mysql/index-invalidation-caused-by-implicit-conversion.md)
|
||||||
|
|
||||||
### Redis
|
### Redis
|
||||||
|
|
||||||
1. [Redis 常见问题总结](docs/database/redis/redis-questions-01.md)
|
- [Redis 常见问题总结](docs/database/redis/redis-questions-01.md)
|
||||||
2. [3种常用的缓存读写策略](docs/database/redis/3-commonly-used-cache-read-and-write-strategies.md)
|
- [3种常用的缓存读写策略](docs/database/redis/3-commonly-used-cache-read-and-write-strategies.md)
|
||||||
2. [Redis 内存碎片](./docs/database/redis/redis-memory-fragmentation.md)
|
- [Redis 内存碎片](./docs/database/redis/redis-memory-fragmentation.md)
|
||||||
|
|
||||||
## 搜索引擎
|
## 搜索引擎
|
||||||
|
|
||||||
|
@ -277,6 +277,7 @@ export const sidebarConfig = defineSidebarConfig({
|
|||||||
"innodb-implementation-of-mvcc",
|
"innodb-implementation-of-mvcc",
|
||||||
"how-sql-executed-in-mysql",
|
"how-sql-executed-in-mysql",
|
||||||
"some-thoughts-on-database-storage-time",
|
"some-thoughts-on-database-storage-time",
|
||||||
|
"index-invalidation-caused-by-implicit-conversion",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -323,7 +324,7 @@ export const sidebarConfig = defineSidebarConfig({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "IDEA",
|
text: "IDEA",
|
||||||
icon:"intellijidea",
|
icon: "intellijidea",
|
||||||
link: "https://gitee.com/SnailClimb/awesome-idea-tutorial",
|
link: "https://gitee.com/SnailClimb/awesome-idea-tutorial",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -115,7 +115,7 @@ tag:
|
|||||||
🙋 **我** :线程同步是两个或多个共享关键资源的线程的并发执行。应该同步线程以避免关键的资源使用冲突。操作系统一般有下面三种线程同步的方式:
|
🙋 **我** :线程同步是两个或多个共享关键资源的线程的并发执行。应该同步线程以避免关键的资源使用冲突。操作系统一般有下面三种线程同步的方式:
|
||||||
|
|
||||||
1. **互斥量(Mutex)**:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。比如 Java 中的 synchronized 关键词和各种 Lock 都是这种机制。
|
1. **互斥量(Mutex)**:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。比如 Java 中的 synchronized 关键词和各种 Lock 都是这种机制。
|
||||||
1. **信号量(Semphares)** :它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。
|
1. **信号量(Semaphore)** :它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。
|
||||||
1. **事件(Event)** :Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作。
|
1. **事件(Event)** :Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作。
|
||||||
|
|
||||||
### 2.5 进程的调度算法
|
### 2.5 进程的调度算法
|
||||||
@ -249,9 +249,9 @@ tag:
|
|||||||
|
|
||||||
简单分为**连续分配管理方式**和**非连续分配管理方式**这两种。连续分配管理方式是指为一个用户程序分配一个连续的内存空间,常见的如 **块式管理** 。同样地,非连续分配管理方式允许一个程序使用的内存分布在离散或者说不相邻的内存中,常见的如**页式管理** 和 **段式管理**。
|
简单分为**连续分配管理方式**和**非连续分配管理方式**这两种。连续分配管理方式是指为一个用户程序分配一个连续的内存空间,常见的如 **块式管理** 。同样地,非连续分配管理方式允许一个程序使用的内存分布在离散或者说不相邻的内存中,常见的如**页式管理** 和 **段式管理**。
|
||||||
|
|
||||||
1. **块式管理** : 远古时代的计算机操系统的内存管理方式。将内存分为几个固定大小的块,每个块中只包含一个进程。如果程序运行需要内存的话,操作系统就分配给它一块,如果程序运行只需要很小的空间的话,分配的这块内存很大一部分几乎被浪费了。这些在每个块中未被利用的空间,我们称之为碎片。
|
1. **块式管理** : 远古时代的计算机操作系统的内存管理方式。将内存分为几个固定大小的块,每个块中只包含一个进程。如果程序运行需要内存的话,操作系统就分配给它一块,如果程序运行只需要很小的空间的话,分配的这块内存很大一部分几乎被浪费了。这些在每个块中未被利用的空间,我们称之为碎片。
|
||||||
2. **页式管理** :把主存分为大小相等且固定的一页一页的形式,页较小,相对相比于块式管理的划分力度更大,提高了内存利用率,减少了碎片。页式管理通过页表对应逻辑地址和物理地址。
|
2. **页式管理** :把主存分为大小相等且固定的一页一页的形式,页较小,相比于块式管理的划分粒度更小,提高了内存利用率,减少了碎片。页式管理通过页表对应逻辑地址和物理地址。
|
||||||
3. **段式管理** : 页式管理虽然提高了内存利用率,但是页式管理其中的页实际并无任何实际意义。 段式管理把主存分为一段段的,段是有实际意义的,每个段定义了一组逻辑信息,例如,有主程序段 MAIN、子程序段 X、数据段 D 及栈段 S 等。 段式管理通过段表对应逻辑地址和物理地址。
|
3. **段式管理** : 页式管理虽然提高了内存利用率,但是页式管理其中的页并无任何实际意义。 段式管理把主存分为一段段的,段是有实际意义的,每个段定义了一组逻辑信息,例如,有主程序段 MAIN、子程序段 X、数据段 D 及栈段 S 等。 段式管理通过段表对应逻辑地址和物理地址。
|
||||||
|
|
||||||
简单来说:页是物理单位,段是逻辑单位。分页可以有效提高内存利用率,分段可以更好满足用户需求。
|
简单来说:页是物理单位,段是逻辑单位。分页可以有效提高内存利用率,分段可以更好满足用户需求。
|
||||||
|
|
||||||
@ -270,7 +270,7 @@ tag:
|
|||||||
|
|
||||||
#### 快表
|
#### 快表
|
||||||
|
|
||||||
为了解决虚拟地址到物理地址的转换速度,操作系统在 **页表方案** 基础之上引入了 **快表** 来加速虚拟地址到物理地址的转换。我们可以把快表理解为一种特殊的高速缓冲存储器(Cache),其中的内容是页表的一部分或者全部内容。作为页表的 Cache,它的作用与页表相似,但是提高了访问速率。由于采用页表做地址转换,读写内存数据时 CPU 要访问两次主存。有了快表,有时只要访问一次高速缓冲存储器,一次主存,这样可加速查找并提高指令执行速度。
|
为了提高虚拟地址到物理地址的转换速度,操作系统在 **页表方案** 基础之上引入了 **快表** 来加速虚拟地址到物理地址的转换。我们可以把快表理解为一种特殊的高速缓冲存储器(Cache),其中的内容是页表的一部分或者全部内容。作为页表的 Cache,它的作用与页表相似,但是提高了访问速率。由于采用页表做地址转换,读写内存数据时 CPU 要访问两次主存。有了快表,有时只要访问一次高速缓冲存储器,一次主存,这样可加速查找并提高指令执行速度。
|
||||||
|
|
||||||
使用快表之后的地址转换流程是这样的:
|
使用快表之后的地址转换流程是这样的:
|
||||||
|
|
||||||
@ -326,7 +326,7 @@ tag:
|
|||||||
|
|
||||||
**为什么要有虚拟地址空间呢?**
|
**为什么要有虚拟地址空间呢?**
|
||||||
|
|
||||||
先从没有虚拟地址空间的时候说起吧!没有虚拟地址空间的时候,**程序都是直接访问和操作的都是物理内存** 。但是这样有什么问题呢?
|
先从没有虚拟地址空间的时候说起吧!没有虚拟地址空间的时候,**程序直接访问和操作的都是物理内存** 。但是这样有什么问题呢?
|
||||||
|
|
||||||
1. 用户程序可以访问任意内存,寻址内存的每个字节,这样就很容易(有意或者无意)破坏操作系统,造成操作系统崩溃。
|
1. 用户程序可以访问任意内存,寻址内存的每个字节,这样就很容易(有意或者无意)破坏操作系统,造成操作系统崩溃。
|
||||||
2. 想要同时运行多个程序特别困难,比如你想同时运行一个微信和一个 QQ 音乐都不行。为什么呢?举个简单的例子:微信在运行的时候给内存地址 1xxx 赋值后,QQ 音乐也同样给内存地址 1xxx 赋值,那么 QQ 音乐对内存的赋值就会覆盖微信之前所赋的值,这就造成了微信这个程序就会崩溃。
|
2. 想要同时运行多个程序特别困难,比如你想同时运行一个微信和一个 QQ 音乐都不行。为什么呢?举个简单的例子:微信在运行的时候给内存地址 1xxx 赋值后,QQ 音乐也同样给内存地址 1xxx 赋值,那么 QQ 音乐对内存的赋值就会覆盖微信之前所赋的值,这就造成了微信这个程序就会崩溃。
|
||||||
@ -345,7 +345,7 @@ tag:
|
|||||||
|
|
||||||
👨💻**面试官** :再问你一个常识性的问题!**什么是虚拟内存(Virtual Memory)?**
|
👨💻**面试官** :再问你一个常识性的问题!**什么是虚拟内存(Virtual Memory)?**
|
||||||
|
|
||||||
🙋 **我** :这个在我们平时使用电脑特别是 Windows 系统的时候太常见了。很多时候我们使用点开了很多占内存的软件,这些软件占用的内存可能已经远远超出了我们电脑本身具有的物理内存。**为什么可以这样呢?** 正是因为 **虚拟内存** 的存在,通过 **虚拟内存** 可以让程序可以拥有超过系统物理内存大小的可用内存空间。另外,**虚拟内存为每个进程提供了一个一致的、私有的地址空间,它让每个进程产生了一种自己在独享主存的错觉(每个进程拥有一片连续完整的内存空间)**。这样会更加有效地管理内存并减少出错。
|
🙋 **我** :这个在我们平时使用电脑特别是 Windows 系统的时候太常见了。很多时候我们使用了很多占内存的软件,这些软件占用的内存可能已经远远超出了我们电脑本身具有的物理内存。**为什么可以这样呢?** 正是因为 **虚拟内存** 的存在,通过 **虚拟内存** 可以让程序可以拥有超过系统物理内存大小的可用内存空间。另外,**虚拟内存为每个进程提供了一个一致的、私有的地址空间,它让每个进程产生了一种自己在独享主存的错觉(每个进程拥有一片连续完整的内存空间)**。这样会更加有效地管理内存并减少出错。
|
||||||
|
|
||||||
**虚拟内存**是计算机系统内存管理的一种技术,我们可以手动设置自己电脑的虚拟内存。不要单纯认为虚拟内存只是“使用硬盘空间来扩展内存“的技术。**虚拟内存的重要意义是它定义了一个连续的虚拟地址空间**,并且 **把内存扩展到硬盘空间**。推荐阅读:[《虚拟内存的那点事儿》](https://juejin.im/post/59f8691b51882534af254317)
|
**虚拟内存**是计算机系统内存管理的一种技术,我们可以手动设置自己电脑的虚拟内存。不要单纯认为虚拟内存只是“使用硬盘空间来扩展内存“的技术。**虚拟内存的重要意义是它定义了一个连续的虚拟地址空间**,并且 **把内存扩展到硬盘空间**。推荐阅读:[《虚拟内存的那点事儿》](https://juejin.im/post/59f8691b51882534af254317)
|
||||||
|
|
||||||
@ -380,7 +380,7 @@ tag:
|
|||||||
|
|
||||||
> 这部分内容来自:[王道考研操作系统知识点整理](https://wizardforcel.gitbooks.io/wangdaokaoyan-os/content/13.html)。
|
> 这部分内容来自:[王道考研操作系统知识点整理](https://wizardforcel.gitbooks.io/wangdaokaoyan-os/content/13.html)。
|
||||||
|
|
||||||
基于局部性原理,在程序装入时,可以将程序的一部分装入内存,而将其他部分留在外存,就可以启动程序执行。由于外存往往比内存大很多,所以我们运行的软件的内存大小实际上是可以比计算机系统实际的内存大小大的。在程序执行过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调入内存,然后继续执行程序。另一方面,操作系统将内存中暂时不使用的内容换到外存上,从而腾出空间存放将要调入内存的信息。这样,计算机好像为用户提供了一个比实际内存大的多的存储器——**虚拟存储器**。
|
基于局部性原理,在程序装入时,可以将程序的一部分装入内存,而将其他部分留在外存,就可以启动程序执行。由于外存往往比内存大很多,所以我们运行的软件的内存大小实际上是可以比计算机系统实际的内存大小大的。在程序执行过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调入内存,然后继续执行程序。另一方面,操作系统将内存中暂时不使用的内容换到外存上,从而腾出空间存放将要调入内存的信息。这样,计算机好像为用户提供了一个比实际内存大得多的存储器——**虚拟存储器**。
|
||||||
|
|
||||||
实际上,我觉得虚拟内存同样是一种时间换空间的策略,你用 CPU 的计算时间,页的调入调出花费的时间,换来了一个虚拟的更大的空间来支持程序的运行。不得不感叹,程序世界几乎不是时间换空间就是空间换时间。
|
实际上,我觉得虚拟内存同样是一种时间换空间的策略,你用 CPU 的计算时间,页的调入调出花费的时间,换来了一个虚拟的更大的空间来支持程序的运行。不得不感叹,程序世界几乎不是时间换空间就是空间换时间。
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: 一条 SQL 语句在 MySQL 中如何被执行的?
|
title: SQL语句在MySQL中的执行过程
|
||||||
category: 数据库
|
category: 数据库
|
||||||
tag:
|
tag:
|
||||||
- MySQL
|
- MySQL
|
||||||
---
|
---
|
||||||
|
|
||||||
本文来自[木木匠](https://github.com/kinglaw1204)投稿。
|
> 本文来自[木木匠](https://github.com/kinglaw1204)投稿。
|
||||||
|
|
||||||
本篇文章会分析下一个 sql 语句在 MySQL 中的执行流程,包括 sql 的查询在 MySQL 内部会怎么流转,sql 语句的更新是怎么完成的。
|
本篇文章会分析下一个 sql 语句在 MySQL 中的执行流程,包括 sql 的查询在 MySQL 内部会怎么流转,sql 语句的更新是怎么完成的。
|
||||||
|
|
||||||
|
@ -0,0 +1,160 @@
|
|||||||
|
---
|
||||||
|
title: MySQL中的隐式转换造成的索引失效
|
||||||
|
category: 数据库
|
||||||
|
tag:
|
||||||
|
- MySQL
|
||||||
|
- 性能优化
|
||||||
|
---
|
||||||
|
|
||||||
|
> 本次测试使用的 MySQL 版本是 `5.7.26`,随着 MySQL 版本的更新某些特性可能会发生改变,本文不代表所述观点和结论于 MySQL 所有版本均准确无误,版本差异请自行甄别。
|
||||||
|
>
|
||||||
|
> 原文:https://www.guitu18.com/post/2019/11/24/61.html
|
||||||
|
|
||||||
|
## 前言
|
||||||
|
|
||||||
|
数据库优化是一个任重而道远的任务,想要做优化必须深入理解数据库的各种特性。在开发过程中我们经常会遇到一些原因很简单但造成的后果却很严重的疑难杂症,这类问题往往还不容易定位,排查费时费力最后发现是一个很小的疏忽造成的,又或者是因为不了解某个技术特性产生的。
|
||||||
|
|
||||||
|
于数据库层面,最常见的恐怕就是索引失效了,且一开始因为数据量小还不易被发现。但随着业务的拓展数据量的提升,性能问题慢慢的就体现出来了,处理不及时还很容易造成雪球效应,最终导致数据库卡死甚至瘫痪。造成索引失效的原因可能有很多种,相关技术博客已经有太多了,今天我要记录的是**隐式转换造成的索引失效**。
|
||||||
|
|
||||||
|
## 数据准备
|
||||||
|
|
||||||
|
首先使用存储过程生成 1000 万条测试数据,
|
||||||
|
测试表一共建立了 7 个字段(包括主键),`num1`和`num2`保存的是和`ID`一样的顺序数字,其中`num2`是字符串类型。
|
||||||
|
`type1`和`type2`保存的都是主键对 5 的取模,目的是模拟实际应用中常用类似 type 类型的数据,但是`type2`是没有建立索引的。
|
||||||
|
`str1`和`str2`都是保存了一个 20 位长度的随机字符串,`str1`不能为`NULL`,`str2`允许为`NULL`,相应的生成测试数据的时候我也会在`str2`字段生产少量`NULL`值(每 100 条数据产生一个`NULL`值)。
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 创建测试数据表
|
||||||
|
DROP TABLE IF EXISTS test1;
|
||||||
|
CREATE TABLE `test1` (
|
||||||
|
`id` int(11) NOT NULL,
|
||||||
|
`num1` int(11) NOT NULL DEFAULT '0',
|
||||||
|
`num2` varchar(11) NOT NULL DEFAULT '',
|
||||||
|
`type1` int(4) NOT NULL DEFAULT '0',
|
||||||
|
`type2` int(4) NOT NULL DEFAULT '0',
|
||||||
|
`str1` varchar(100) NOT NULL DEFAULT '',
|
||||||
|
`str2` varchar(100) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `num1` (`num1`),
|
||||||
|
KEY `num2` (`num2`),
|
||||||
|
KEY `type1` (`type1`),
|
||||||
|
KEY `str1` (`str1`),
|
||||||
|
KEY `str2` (`str2`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
-- 创建存储过程
|
||||||
|
DROP PROCEDURE IF EXISTS pre_test1;
|
||||||
|
DELIMITER //
|
||||||
|
CREATE PROCEDURE `pre_test1`()
|
||||||
|
BEGIN
|
||||||
|
DECLARE i INT DEFAULT 0;
|
||||||
|
SET autocommit = 0;
|
||||||
|
WHILE i < 10000000 DO
|
||||||
|
SET i = i + 1;
|
||||||
|
SET @str1 = SUBSTRING(MD5(RAND()),1,20);
|
||||||
|
-- 每100条数据str2产生一个null值
|
||||||
|
IF i % 100 = 0 THEN
|
||||||
|
SET @str2 = NULL;
|
||||||
|
ELSE
|
||||||
|
SET @str2 = @str1;
|
||||||
|
END IF;
|
||||||
|
INSERT INTO test1 (`id`, `num1`, `num2`,
|
||||||
|
`type1`, `type2`, `str1`, `str2`)
|
||||||
|
VALUES (CONCAT('', i), CONCAT('', i),
|
||||||
|
CONCAT('', i), i%5, i%5, @str1, @str2);
|
||||||
|
-- 事务优化,每一万条数据提交一次事务
|
||||||
|
IF i % 10000 = 0 THEN
|
||||||
|
COMMIT;
|
||||||
|
END IF;
|
||||||
|
END WHILE;
|
||||||
|
END;
|
||||||
|
// DELIMITER ;
|
||||||
|
-- 执行存储过程
|
||||||
|
CALL pre_test1();
|
||||||
|
```
|
||||||
|
|
||||||
|
数据量比较大,还涉及使用`MD5`生成随机字符串,所以速度有点慢,稍安勿躁,耐心等待即可。
|
||||||
|
|
||||||
|
1000 万条数据,我用了 33 分钟才跑完(实际时间跟你电脑硬件配置有关)。这里贴几条生成的数据,大致长这样。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## SQL 测试
|
||||||
|
|
||||||
|
先来看这组 SQL,一共四条,我们的测试数据表`num1`是`int`类型,`num2`是`varchar`类型,但是存储的数据都是跟主键`id`一样的顺序数字,两个字段都建立有索引。
|
||||||
|
|
||||||
|
```sql
|
||||||
|
1: SELECT * FROM `test1` WHERE num1 = 10000;
|
||||||
|
2: SELECT * FROM `test1` WHERE num1 = '10000';
|
||||||
|
3: SELECT * FROM `test1` WHERE num2 = 10000;
|
||||||
|
4: SELECT * FROM `test1` WHERE num2 = '10000';
|
||||||
|
```
|
||||||
|
|
||||||
|
这四条 SQL 都是有针对性写的,12 查询的字段是 int 类型,34 查询的字段是`varchar`类型。12 或 34 查询的字段虽然都相同,但是一个条件是数字,一个条件是用引号引起来的字符串。这样做有什么区别呢?先不看下边的测试结果你能猜出这四条 SQL 的效率顺序吗?
|
||||||
|
|
||||||
|
经测试这四条 SQL 最后的执行结果却相差很大,其中 124 三条 SQL 基本都是瞬间出结果,大概在 0.001~0.005 秒,在千万级的数据量下这样的结果可以判定这三条 SQL 性能基本没差别了。但是第三条 SQL,多次测试耗时基本在 4.5~4.8 秒之间。
|
||||||
|
|
||||||
|
为什么 34 两条 SQL 效率相差那么大,但是同样做对比的 12 两条 SQL 却没什么差别呢?查看一下执行计划,下边分别 1234 条 SQL 的执行计划数据:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
可以看到,124 三条 SQL 都能使用到索引,连接类型都为`ref`,扫描行数都为 1,所以效率非常高。再看看第三条 SQL,没有用上索引,所以为全表扫描,`rows`直接到达 1000 万了,所以性能差别才那么大。
|
||||||
|
|
||||||
|
仔细观察你会发现,34 两条 SQL 查询的字段`num2`是`varchar`类型的,查询条件等号右边加引号的第 4 条 SQL 是用到索引的,那么是查询的数据类型和字段数据类型不一致造成的吗?如果是这样那 12 两条 SQL 查询的字段`num1`是`int`类型,但是第 2 条 SQL 查询条件右边加了引号为什么还能用上索引呢。
|
||||||
|
|
||||||
|
查阅 MySQL 相关文档发现是隐式转换造成的,看一下官方的描述:
|
||||||
|
|
||||||
|
> 官方文档: [12.2 Type Conversion in Expression Evaluation](https://dev.mysql.com/doc/refman/5.7/en/type-conversion.html?spm=5176.100239.blogcont47339.5.1FTben)
|
||||||
|
>
|
||||||
|
> 当操作符与不同类型的操作数一起使用时,会发生类型转换以使操作数兼容。某些转换是隐式发生的。例如,MySQL 会根据需要自动将字符串转换为数字,反之亦然。以下规则描述了比较操作的转换方式:
|
||||||
|
>
|
||||||
|
> 1. 两个参数至少有一个是`NULL`时,比较的结果也是`NULL`,特殊的情况是使用`<=>`对两个`NULL`做比较时会返回`1`,这两种情况都不需要做类型转换
|
||||||
|
> 2. 两个参数都是字符串,会按照字符串来比较,不做类型转换
|
||||||
|
> 3. 两个参数都是整数,按照整数来比较,不做类型转换
|
||||||
|
> 4. 十六进制的值和非数字做比较时,会被当做二进制串
|
||||||
|
> 5. 有一个参数是`TIMESTAMP`或`DATETIME`,并且另外一个参数是常量,常量会被转换为`timestamp`
|
||||||
|
> 6. 有一个参数是`decimal`类型,如果另外一个参数是`decimal`或者整数,会将整数转换为`decimal`后进行比较,如果另外一个参数是浮点数,则会把`decimal`转换为浮点数进行比较
|
||||||
|
> 7. **所有其他情况下,两个参数都会被转换为浮点数再进行比较**
|
||||||
|
|
||||||
|
根据官方文档的描述,我们的第 23 两条 SQL 都发生了隐式转换,第 2 条 SQL 的查询条件`num1 = '10000'`,左边是`int`类型右边是字符串,第 3 条 SQL 相反,那么根据官方转换规则第 7 条,左右两边都会转换为浮点数再进行比较。
|
||||||
|
|
||||||
|
先看第 2 条 SQL:`SELECT * FROM`test1`WHERE num1 = '10000';` **左边为 int 类型**`10000`,转换为浮点数还是`10000`,右边字符串类型`'10000'`,转换为浮点数也是`10000`。两边的转换结果都是唯一确定的,所以不影响使用索引。
|
||||||
|
|
||||||
|
第 3 条 SQL:`SELECT * FROM`test1`WHERE num2 = 10000;` **左边是字符串类型**`'10000'`,转浮点数为 10000 是唯一的,右边`int`类型`10000`转换结果也是唯一的。但是,因为左边是检索条件,`'10000'`转到`10000`虽然是唯一,但是其他字符串也可以转换为`10000`,比如`'10000a'`,`'010000'`,`'10000'`等等都能转为浮点数`10000`,这样的情况下,是不能用到索引的。
|
||||||
|
|
||||||
|
关于这个**隐式转换**我们可以通过查询测试验证一下,先插入几条数据,其中`num2='10000a'`、`'010000'`和`'10000'`:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
INSERT INTO `test1` (`id`, `num1`, `num2`, `type1`, `type2`, `str1`, `str2`) VALUES ('10000001', '10000', '10000a', '0', '0', '2df3d9465ty2e4hd523', '2df3d9465ty2e4hd523');
|
||||||
|
INSERT INTO `test1` (`id`, `num1`, `num2`, `type1`, `type2`, `str1`, `str2`) VALUES ('10000002', '10000', '010000', '0', '0', '2df3d9465ty2e4hd523', '2df3d9465ty2e4hd523');
|
||||||
|
INSERT INTO `test1` (`id`, `num1`, `num2`, `type1`, `type2`, `str1`, `str2`) VALUES ('10000003', '10000', ' 10000', '0', '0', '2df3d9465ty2e4hd523', '2df3d9465ty2e4hd523');
|
||||||
|
```
|
||||||
|
|
||||||
|
然后使用第三条 SQL 语句`SELECT * FROM`test1`WHERE num2 = 10000;`进行查询:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
从结果可以看到,后面插入的三条数据也都匹配上了。那么这个字符串隐式转换的规则是什么呢?为什么`num2='10000a'`、`'010000'`和`'10000'`这三种情形都能匹配上呢?查阅相关资料发现规则如下:
|
||||||
|
|
||||||
|
1. **不以数字开头**的字符串都将转换为`0`。如`'abc'`、`'a123bc'`、`'abc123'`都会转化为`0`;
|
||||||
|
2. **以数字开头的**字符串转换时会进行截取,从第一个字符截取到第一个非数字内容为止。比如`'123abc'`会转换为`123`,`'012abc'`会转换为`012`也就是`12`,`'5.3a66b78c'`会转换为`5.3`,其他同理。
|
||||||
|
|
||||||
|
现对以上规则做如下测试验证:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
如此也就印证了之前的查询结果了。
|
||||||
|
|
||||||
|
再次写一条 SQL 查询 str1 字段:`SELECT * FROM`test1`WHERE str1 = 1234;`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 分析和总结
|
||||||
|
|
||||||
|
通过上面的测试我们发现 MySQL 使用操作符的一些特性:
|
||||||
|
|
||||||
|
1. 当操作符**左右两边的数据类型不一致**时,会发生**隐式转换**。
|
||||||
|
2. 当 where 查询操作符**左边为数值类型**时发生了隐式转换,那么对效率影响不大,但还是不推荐这么做。
|
||||||
|
3. 当 where 查询操作符**左边为字符类型**时发生了隐式转换,那么会导致索引失效,造成全表扫描效率极低。
|
||||||
|
4. 字符串转换为数值类型时,非数字开头的字符串会转化为`0`,以数字开头的字符串会截取从第一个字符到第一个非数字内容为止的值为转化结果。
|
||||||
|
|
||||||
|
所以,我们在写 SQL 时一定要养成良好的习惯,查询的字段是什么类型,等号右边的条件就写成对应的类型。特别当查询的字段是字符串时,等号右边的条件一定要用引号引起来标明这是一个字符串,否则会造成索引失效触发全表扫描。
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: MySQL 高性能优化规范建议
|
title: MySQL高性能优化规范建议
|
||||||
category: 数据库
|
category: 数据库
|
||||||
tag:
|
tag:
|
||||||
- MySQL
|
- MySQL
|
||||||
@ -252,10 +252,12 @@ Innodb 是按照主键索引的顺序来组织表的
|
|||||||
|
|
||||||
隐式转换会导致索引失效如:
|
隐式转换会导致索引失效如:
|
||||||
|
|
||||||
```
|
```sql
|
||||||
select name,phone from customer where id = '111';
|
select name,phone from customer where id = '111';
|
||||||
```
|
```
|
||||||
|
|
||||||
|
详细解读可以看:[MySQL中的隐式转换造成的索引失效](./index-invalidation-caused-by-implicit-conversion.md) 这篇文章。
|
||||||
|
|
||||||
### 3. 充分利用表上已经存在的索引
|
### 3. 充分利用表上已经存在的索引
|
||||||
|
|
||||||
避免使用双%号的查询条件。如:`a like '%123%'`,(如果无前置%,只有后置%,是可以用到列上的索引的)
|
避免使用双%号的查询条件。如:`a like '%123%'`,(如果无前置%,只有后置%,是可以用到列上的索引的)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: MySQL 索引详解
|
title: MySQL索引详解
|
||||||
category: 数据库
|
category: 数据库
|
||||||
tag:
|
tag:
|
||||||
- MySQL
|
- MySQL
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: 关于数据库中如何存储时间的一点思考
|
title: MySQL数据库时间类型数据存储建议
|
||||||
category: 数据库
|
category: 数据库
|
||||||
tag:
|
tag:
|
||||||
- MySQL
|
- MySQL
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: 事务隔离级别(图文详解)
|
title: MySQL事务隔离级别详解
|
||||||
category: 数据库
|
category: 数据库
|
||||||
tag:
|
tag:
|
||||||
- MySQL
|
- MySQL
|
||||||
|
@ -122,7 +122,7 @@ dataLength = 11
|
|||||||
numChildren = 1
|
numChildren = 1
|
||||||
```
|
```
|
||||||
|
|
||||||
上面显示的一些信息比如 cversion、aclVersion、numChildren 等等,我在上面 “znode(数据节点)的结构” 这部分已经介绍到。
|
上面显示的一些信息比如 cversion、aclVersion、numChildren 等等,我在上面 “[ZooKeeper 相关概念总结(入门)](https://javaguide.cn/distributed-system/distributed-process-coordination/zookeeper/zookeeper-intro.html)” 这篇文章中已经介绍到。
|
||||||
|
|
||||||
#### 2.3.7. 查看节点信息和状态(ls2 命令)
|
#### 2.3.7. 查看节点信息和状态(ls2 命令)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user