1.MySQL的存储引擎有哪几种?


存储引擎 :对于数据库文件的一种存储机制,如何实现存储数据,如何为存储的数据建立索引以及如何更新,查询数据等技术实现的方法。
MySQL中查看引擎:
**show engines;** // 查看mysql所支持的存储引擎,以及从中得到mysql的默认存储引擎。
image.png
show variables like '% storage_engine'; // 查看mysql默认的存储引擎
show create table <tablename>; // 查看具体一个表所使用的存储引擎
show table status from database where name="tablename"; // 准确查看某个数据库中的某一个表所使用的存储引擎

InnoDB存储引擎: 支持事务处理,支持外键,支持崩溃修复能力和并发控制。如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),那选择InnoDB有很大的优势。如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB,因为支持事务的提交(commit)和回滚(rollback)。 MyISAM存储引擎: 插入数据快,空间和内存使用比较低。如果表主要是用于插入新记录和读出记录,那么选择MyISAM能实现处理高效率。如果应用的完整性、并发性要求比 较低,也可以使用。 MEMORY存储引擎: 所有的数据都在内存中,数据的处理速度快,但是安全性不高。如果需要很快的读写速度,对数据的安全性要求较低,可以选择MEMOEY。它对表的大小有要求,不能建立太大的表。所以,这类数据库只使用在相对较小的数据库表。

注意,同一个数据库也可以使用多种存储引擎的表。如果一个表要求比较高的事务处理,可以选择InnoDB。这个数据库中可以将查询要求比较高的表选择MyISAM存储。如果该数据库需要一个用于查询的临时表,可以选择MEMORY存储引擎。

2. InnoDB和MyISAM的区别


数据库 - 图2

3. 并发事务会发生的问题


参考

事务 Transaction:

并发控制的基本单位。它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。例如,银行转账工作:从一个账号扣款并使另一个账号增款,这两个操作要么都执行,要么都不执行。
事务是数据库维护数据一致性的单位,再每个事务结束时,都能保持数据一致性。
数据库 - 图3

事务的四大特性 ACID

  • 原子性 Atomicity:一个事务是不可分割的,是最小的执行单位。
  • 一致性 Consistency:在执行事务后,数据库状态保持一致,从一个一致性状态转换到另一个一致性状态。
  • 隔离性 Isolation:在并发访问数据库时,一个事务不被其他事务所干扰,各并发事务之间数据库是独立的。隔离性是通过锁来实现的。
  • 持久性 Durability:一旦事务提交,则其所做的修改就会永久保存到数据库中。即使数据库发生故障也不应该对其有任何影响。

4. 事务的隔离级别


https://developer.aliyun.com/article/743691

1 事务并发可能发生的错误情况

丢失更新 (第一类丢失更新 回滚丢失 lost update)

定义:A事务**撤销**时,把已经提交的B事务的更新数据覆盖了

时间点 事务A 事务B
T1 开始事务
T2 开始事务
T3 查询账户余额为1000元
T4 查询账户余额为1000元
T5 存入100元把余额改为1100元
T6 提交事务
T7 取出100元把余额改为900元
T8 撤销事务
T9 余额恢复为1000元(丢失更新)

事务A的开始时间和结束时间包括事务B,事务A回滚的同时把B已经提交的事务也回滚。

第二类丢失更新 (覆盖更新/两次更新问题 second lost update)

定义:A事务**提交**的时候,把已经提交的事务B的更新覆盖了

时间点 事务A 事务B
T1 开始事务
T2 开始事务
T3 查询账户余额为1000元
T4 查询账户余额为1000元
T5 取出100元把余额改为900元
T6 提交事务
T7 存入100元把余额改为1100
T8 提交事务
T9 余额恢复为1100元(丢失更新)

第二类丢失更新和第一类的区别实际上是对数据的影响是**由A事务的撤销还是提交造成的**,它和不可重复读(下面介绍)本质上是同一类并发问题,通常把它看做是不可重复读的一个特例。两个或多个事务查询同一数据。然后都基于自己的查询结果更新数据,这时会造成最后一个提交的更新事务,将覆盖其它已经提交的更新事务。

脏读 (Dirty read)

定义:一个事务**读到了另一个未提交事务修改过**的数据(脏读只在读未提交隔离级别才会出现)


时间点 事务A 事务B
T1 开始事务
T2 开始事务
T3 查询账户余额为1000元
T4 取出500元把余额改为500元
T5 查询账户余额为500元(脏读)
T6 撤销事务,余额恢复为1000元
T7 存入100元把余额改为600元
T8 提交事务

数据表中的数据是实时改变的,事务只是控制数据的最终状态,也就是说如果没有正确的隔离级别,在更新操作语句结束后,即使事务未完成,其他事务就已经可以读取到改变的数据值了。

不可重复读 (Non-Repeatable Read)

定义:一个事务**只能读到另一个已经提交的事务修改过的数据**,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值。(不可重复读在读未提交和读已提交隔离级别都可能会出现)

时间点 事务A 事务B
T1 开始事务
T2 开始事务
T3 查询账户余额为1000元
T4 查询账户余额为1000元
T5 取出100元把余额改为900元
T6 提交事务
T7 查询账户余额为900元(与T4读取的一不一致,不可重复读)

幻读 (Phantom)

定义:一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把**另一个事务插入的记录也读出来**。(幻读在读未提交、读已提交、可重复读隔离级别都可能会出现)

数据库 - 图4

会话A开启一个事务,查询id>0的记录,此时会查到name=武汉市的记录。接着会话B插入一条name=温州市的数据(隐式事务,因为此时的autocommit为1,每条SQL语句执行完自动提交),这时会话A的事务再以刚才的查询条件(id>0)再一次查询,此时会出现两条记录(name为武汉市和温州市的记录),这种现象就是幻读。

2 事务的隔离级别

MySQL的隔离级别作用是: 让事务之间彼此隔离,互不影响保持事务**一致性 SQL标准定义了4类隔离级别,包括一些具体规则,用来限定事务内务的哪些改变是可见的,哪些是不可见的低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。 隔离级别越高,所需要消耗的MySQL性能越大(如事务并发严重性),为了平衡二者,一般建议设置的隔离级别为可重复读,**MySQL默认的隔离级别也是可重复读**。

SERIALIZABLE 串行化 最安全/最慢

一个事务在执行过程中**完全看不到其他事务对数据库所作的更新**,当事务AB同时操作数据库中相同数据D时,如果A正在访问D,B只能停下来等待,必须等到A结束才能恢复运行。因此AB是串行化方式运行。

REPEATABLE READ 可重复读 2

一个事务在执行过程中**可以看到其他事务已经提交的新插入**的记录,但是不能看到其他事务对已有记录的更新。

READ COMMITED 读已提交数据 3

一个事务在执行过程中可**以看到其他事务已经提交的新插入记录,而且还能看到其他事务已经提交的对已有记录的更新**

READ UNCOMMITED 读未提交数据 最不安全/最快

一个事务在执行过程中**可以看到其他事务没有提交的新插入的记录,而且还能看到其他事务没有提交的对已有记录的更新**。

隔离级别 第一类
丢失更新
第二类
丢失更新
脏读 不可重复读 幻读
SERIALIZABLE
(串行化)
避免 避免 避免 避免 避免
REPEATABLE READ(可重复读) 避免 避免 避免 避免 允许
READ COMMITTED
(读已提交)
避免 允许 避免 允许 允许
READ UNCOMMITTED
(读未提交)
避免 允许 允许 允许 允许

3 隔离的实现原理

对于可重复读 每条记录在更新的时候会同时记录一条回滚操作(undo log)。同一条记录在系统中可以存在多个版本,这就是数据库版的多版本并发控制(MVCC) 通过回滚rollback操作回到上一个状态值。 提问:回滚操作日志(undo log)什么时候删除?** MySQL会判断当没有事务需要用到这些回滚日志的时候,回滚日志会被删除。

提问:什么时候不需要了? 当系统里么有比这个回滚日志更早的read-view的时候。

4 查看当前会话隔离级别

  1. show variables like 'transaction_isolation';
  2. Variable_name |Value |
  3. ---------------------|---------------|
  4. transaction_isolation|REPEATABLE-READ|
  5. select @@transaction_isolation;
  6. @@transaction_isolation|
  7. -----------------------|
  8. REPEATABLE-READ |

5 设置隔离级别

set [global|session] transaction isolation level <level>;

其中level有4种值:
level: {
     REPEATABLE READ
   | READ COMMITTED
   | READ UNCOMMITTED
   | SERIALIZABLE
}

关键词:GLOBAL
SET GLOBAL TRANSACTION ISOLATION LEVEL level;
* 只对执行完该语句之后产生的会话起作用
* 当前已经存在的会话无效

关键词:SESSION
SET SESSION TRANSACTION ISOLATION LEVEL level;
* 对当前会话的所有后续的事务有效
* 该语句可以在已经开启的事务中间执行,但不会影响当前正在执行的事务
* 如果在事务之间执行,则对后续的事务有效。

无关键词
SET TRANSACTION ISOLATION LEVEL level;
* 只对当前会话中下一个即将开启的事务有效
* 下一个事务执行完后,后续事务将恢复到之前的隔离级别
* 该语句不能在已经开启的事务中间执行,会报错的

5. MySQL中的锁


https://learnku.com/articles/39212?order_by=vote_count&

锁是什么

计算机协调多个进程或线程并发访问某一资源的机制

锁的重要性

在数据库中,除了CPU/RAM/I/O等资源的争夺外,数据也是一种供多用户共享的资源。 如何保证数据并发访问的一致性有效性是所有数据库必须要解决的问题 锁冲突也就是影响数据库并发访问性能的一个重要原因。
锁是用于管理对公共资源的并发控制 ** **

锁的缺点

加锁是消耗资源的。 锁的各种操作都会增加系统开销

锁的分类

表锁

  • 读锁||共享锁(Read Lock/Shared Lock): 针对同一份数据多个读操作可以同时进行而不会互相影响 select
  • 写锁||排他锁(write lock/Exclusive Lock):当前操作没有完成之前,会阻塞其他读写操作 update insert delete
  • 存储引擎默认锁 MyISAM :写优先不适合作为主表
  1. 对整张表加锁
    2. 开销小
    3. 加锁快
    4. 无死锁
    5. 锁粒度大,发生锁冲突概率大,并发性低

    行锁

    • 共享锁||读锁||S锁(Shared Lock) :其他事务可以读不可以写。是允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁
    • 排他锁||写锁||X锁(Exclusive):其他事务不能读取也不能写。允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。

    类型细分:

    • 意向共享锁 IS Lock/Intent Share Lock : 一个事务给一个数据行加共享锁时,必须先获得表的IS锁
    • 意向排他锁||互斥锁 IX Lock/Intent Exclusive Lock:一个事务给一盒数据行加排他锁时,必须先获得改表的IX锁
      • 悲观锁||保守锁(Pessimistic Locking) : 假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作

    悲观锁是数据库层面加锁,会阻塞去等待锁

    • 乐观锁(Optimistic Locking):假设不会发生并发冲突,只是在提交操作时检查是否违反数据完整性。

    缺点:并发很高的时候,多了很多无用的重试。乐观锁,不能解决脏读的问题。

  2. 对一行数据加锁
    2. 开销大
    3. 加锁慢
    4. 会出现死锁
    5. 锁粒度小,发生锁冲突概率最低,并发性高
    数据库 - 图5

    如何上锁

    表锁:

    • 隐式上锁(默认自动加/放锁):

    select 读锁 insert update insert 写锁

    • 显式上锁:

    lock the table <tablename> read lock the table <tablename> write;

    • 解锁:

    unlock tables;

行锁:

  • 隐式:

select 不会上锁 insert update insert 写锁

  • 显式上锁

select * from <tablename> lock in share mode; 读锁 select * from <tablename> for update 写锁

  • 解锁
  1. 提交事务(commit)2. 回滚事务(rollback)3. kill 阻塞进程

锁的优化

  1. 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
  2. 合理设计索引,尽量缩小锁的范围
  3. 尽可能较少检索条件,避免间隙锁
  4. 尽量控制事务大小,减少锁定资源量和时间长度
  5. 尽可能低级别事务隔离

死锁

指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶行循环的现象

死锁条件

  1. 互斥条件:一个资源每次只能由一个进程使用
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不剥夺条件:进程已获得的资源,在没有使用完之前,不能强行剥夺
  4. 循环等待条件:多个进程之间形成一种互相循环等待的资源的关系

解锁死锁

查看死锁 show engine innodb status \G , show processlist 然后杀死进程 kill

避免

  1. 加锁顺序一致,尽可能一次性锁定所需的数据行
  2. 尽量基于primary(主键)或unique key更新数据
  3. 单次操作数据量不宜过多,涉及表尽量少
  4. 减少表上索引,减少锁定资源
  5. 尽量使用较低的隔离级别
  6. 尽量使用相同条件访问数据,这样可以避免间隙锁对并发的插入影响
  7. 精心设计索引,尽量使用索引访问数据
  8. 借助相关工具:pt-deadlock-logger

6. 主从复制


概念:

主从复制是指一台服务器充当主数据库服务器,另一台或多台服务器充当从数据库服务器,主服务器中的数据自动复制到服务器之中。从一个MySQL数据库服务器主节点复制到一个或多个从节点。 MySQL 默认采用异步复制方式,这样从节点不用一直访问主服务器来更新自己的数据,数据的更新可以在远程连接上进行,从节点可以复制主数据库中的所有数据库或者特定的数据库,或者特定的表。

为什么需要主从复制

  • 做数据的热备份
  • 如果主数据库宕机,可以快速将业务切换到从数据库中,避免数据丢失
  • I/O访问频率过多,多库存储降低磁盘I/O访问频率,提高单个机器的I/O性能;如果对数据库的读写都在同一个数据库服务器中操作,业务系统性能降低。
  • 主库负责写,从库负责读,在主库出现锁表的情况下也可以读从库保证业务正常运作;通过主从复制(读写分离)来减轻主数据库的负载

主从复制用途

  • 读写分离——针对锁表
  • 热备份——防宕机
  • 高可用 (HA, High Availability)架构扩展——I/O性能

主从复制的类型:

  • 基于语句的复制(默认):时间上可能不完全同步造成偏差,执行语句的用于也可能不是同一个用户
  • 基于行的复制:修改的行多会造成大开销

主从复制原理:

数据库 - 图6
主服务器上的任何更改都会保存在二进制日志Binary log中,从从服务器上启动一个 I/O Thread,连接到主服务器上请求read Binary log,然后把读取的binary log存储到本地的一个Relay log(中继日志)中。在从服务器上开启一个SQL Thread定时检查Relay log,如果发现有更改立即把更改的内容在本机上执行。

主从复制过程:

数据库 - 图7

  • Slave上面的IO进程连接上Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容。
  • Master接收到来自Slave的IO进程的请求后,负责复制的IO进程会根据请求信息读取日志指定位置之后的日志信息,返回给Slave的IO进程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息已经到Master端的bin-log文件的名称以及bin-log的位置。
  • Slave的IO进程接收到信息后,将接收到的日志内容依次添加到Slave端的relay-log文件的最末端,并将读取到的Master端的 bin-log的文件名和位置记录到master-info文件中,以便在下一次读取的时候能够清楚的告诉Master从何处开始读取日志。
  • Slave的Sql进程检测到relay-log中新增加了内容后,会马上解析relay-log的内容成为在Master端真实执行时候的那些可执行的内容,并在自身执行

7. 主从复制的半同步和全同步

异步模式 async-mode (默认)

异步模式,主库将事务bin log写入到 Binary log中,此时主库只会通知Dump Thread发送新的Binary log,然后主库就会继续处理提交操作,而此时不能保证这些Binary log传到任何一个从库节点。这种模式下,主节点不会主动push bin log到从节点,这样有可能导致failover的情况下,也许从节点没有即时地将最新的bin log同步到本地。

数据库 - 图8

半同步模式 semi-sync-mode 效率最高

主库只需要等待至少一个从库节点收到并且 Flush Binlog 到 Relay Log 文件即可,主库不需要等待所有从库给主库反馈。同时,这里只是一个收到的反馈,而不是已经完全完成并且提交的反馈,如此,节省了很多时间。 半同步复制有两种方案(用参数rpl_semi_sync_master_wait_point来控制): 1)after_commit:旧的半同步方案,特殊情况下会出现幻读,导致有数据丢失。 2)after_sync: mysql5.7之后新的半同步方案,默认值为after_sync,可实现无损复制 可以提高数据安全性,性能上会有一定的降低,响应时间会变长

数据库 - 图9

全同步模式 sync-mode

全同步模式是指主节点和从节点全部执行了commit并确认才会向客户端返回成功。当主库提交事务之后,所有的从库节点必须收到、APPLY并且提交这些事务,然后主库线程才能继续做后续操作。但缺点是,主库完成一个事务的时间会被拉长,性能降低。

8. 查询语句不同元素(where、jion、limit、group by、having等等)执行先后顺序?


1.查询中用到的关键词主要包含六个,并且他们的顺序依次为 select--from--where--group by--having--order by

其中select和from是必须的,其他关键词是可选的,这六个关键词的执行顺序 与sql语句的书写顺序并不是一样的,而是按照下面的顺序来执行

  • from :需要从哪个数据表检索数据
  • where :过滤表中数据的条件
  • group by :如何将上面过滤出的数据分组
  • having :对上面已经分组的数据进行过滤的条件
  • select :查看结果集中的哪个列,或列的计算结果
  • order by :按照什么样的顺序来查看返回的数据

2.from后面的表关联,是自右向左解析 而where条件的解析顺序是自下而上的。
也就是说,在写SQL文的时候,尽量把数据量小的表放在最右边来进行关联(用小表去匹配大表),而把能筛选出小量数据的条件放在where语句的最左边 (用小表去匹配大表)

9. 数据库量增大怎么优化查询


单表优化

1 表分区

2 增加缓存

3 字段设计:
  • 单表不要有太多字段;
  • varchar的长度尽量只分配真正需要的空间;
  • 尽量使用timestamp而非datetime;
  • 避免使用NULL,可以通过设置默认值解决;

4 索引优化
  • 索引不是越多越好,针对性地简历索引,索引会加速查询,但对新增、修改、删除会造成一定影响;
  • 值域很少的字段不适合键索引;
  • 尽量不用UNIQUE,不要设置外键,由程序保障;

表拆分

1 垂直拆分

把一个字段较多的表,拆分成多个字段较少的表

2 水平拆分

分表分库

10. SQL调优怎么操作

11. 数据库索引的实现


索引的数据结构——B+树

https://tech.meituan.com/2014/06/30/mysql-index.html

数据库 - 图10
如上图,是一颗b+树,关于b+树的定义可以参见B+树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项**,如17、35并不真实存在于数据表中。**

B+树的查找过程

如图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。

B+树的性质

1.通过上面的分析,我们知道IO次数取决于b+数的高度h,假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为什么每个数据项,即索引字段要尽量的小,比如int占4字节,要比bigint8字节少一半。这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。当数据项等于1时将会退化成线性表。
2.当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性

12. 慢查询优化


建立索引的几大原则

1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。 2.=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式。 3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录。 4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’)。 5.尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要*加(a,b)的索引,那么只需要修改原来的索引即可。

慢查询优化基本步骤

0.先运行看看是否真的很慢,注意设置SQL_NO_CACHE 1.where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高 2.explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询) 3.order by limit 形式的sql语句让排序的表优先查 4.了解业务方使用场景 5.加索引时参照建索引的几大原则 6.观察结果,不符合预期继续从0分析

mysql中的索引,主要就用在where条件中和排序动作中。分两种情况。
1、先过滤,再排序,会用到过滤条件中的索引参数,但是排序会使用较慢的外部排序。因为这个结果集是经过过滤的,并没有什么索引参与。
2、先排序,再过滤,可以使用同一个索引,排序的优先级高于过滤的优先级。选择合适的索引,在过滤的同时就把这个事给办了。但是扫描的行数会增加。
我想,mysql并不能够了解到这两个过程,到底谁快谁慢,于是选了一个最通用的方式,直接选用了第一种。甚至在索引非常多的时候,直接晕菜了。索引建多了,你可能间接把mysql给害了。这是现象,至于深层次的原因,欢迎读过mysql相关源码的给解释一下。
这对经常变换字段进行排序的代码来说,并不是一个好的信号。考虑到程序的稳定性,我想应该要尽量减少where条件过滤后的结果集。这种情况下,创建一个(a,b)的联合索引,或许是一个折衷的方式。

13. truncate drop delete区别


1 用法:

delete from <tablename> where <condition>

delete是DML语句,删除所有符合条件的数据,但保留表的结构;不释放所占空间

drop table <tablename>

drop是DDL语句,删除时,清除所有表数据也删除表结构,释放所占空间。 **直接删掉表**

truncate table <tablename>

truncate是DDL语句,删除时,清除表所有数据,保留表结构;释放所占空间 ,再插入数据时自增长的数据id又重新从1开始

2 执行效率

drop > truncate > delete
delete执行过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录再日志中保存以便进行回滚操作;
truncate table则是一次性从表中删除所有数据,删除行不可恢复。

对于由 FOREIGN KEY 约束引用的表,不能使用 TRUNCATE TABLE,而应使用不带 WHERE 子句的 DELETE 语句。由于 TRUNCATE TABLE 不记录在日志中,所以它不能激活触发器。

3 事务方面

delete 在未提交事务前,可以事务回滚rollback

drop 隐性事务结束,不能事务回滚rollback

truncate 隐性事务结束,不能事务回滚rollback

14. DQL DML DDL


1 数据查询语言 Date Query Language DQL

用于从表中获取数据。通常最常用的为保留字 select ,并且常与 from where 子句组合成SQL查询语句

2 数据操纵语言 Data Manipulation Language DML

数据操纵语句,用于添加、删除、更新和查询数据库记录,并检查数据完整性.

查询和更新指令构成了 SQL 的 DML 部分:
SELECT - 从数据库表中获取数据
UPDATE - 更新数据库表中的数据
DELETE - 从数据库表中删除数据
INSERT INTO - 向数据库表中插入数据

3 数据定义语言 Data Define Language DDL

可以创建或删除表,也可以定义索引键,规定表之间的连接,以及施加表的约束

CREATE DATABASE - 创建新数据库
ALTER DATABASE - 修改数据库
CREATE TABLE - 创建新表
ALTER TABLE - 变更(改变)数据库表
DROP TABLE - 删除表
CREATE INDEX - 创建索引(搜索键)
DROP INDEX - 删除索引

15. join


join子句主要用于把两个或者多个表的行结合起来,基于这些表之间的共同字段。主要有内连接 inner join,外连接 left join , right join , full join

两张表 Com和Region

id name
1 Google
2 淘宝
3 微博
4 Facebook
id address
1 美国
5 中国
3 中国
6 美国

inner join / join 内连接 交集

select column_name(s)
from table_1
inner join table_2
on table_1.column_name = table_2.column_name

数据库 - 图11

-- 内连接 inner join  / join 交集
select com.id,com.name,region.address 
from com join region 
on com.id = region.id;
id name address
1 Google 美国
3 微博 中国

left join 外连接 左

返回左表的全部行和右表满足on条件的行。如果左表行在右表中没有匹配,
— 那么这一行右表中对应数据用NULL表示。

select column_name(s)
from table_1
left join table_2
on table_1.column_name = table_2.column_name

数据库 - 图12

select com.id,com.name,region.address
from com left join region 
on com.id = region.id;
id name address
1 Google 美国
2 淘宝 null
3 微博 中国
4 Facebook null

right join 外连接 右

返回右表中全部行和左表满足on条件的行,如果右表的行在左表中没有匹配,
— 左表行用NULL代替。

select column_name(s)
from table_1
right join table_2
on table_1.column_name = table2.column_name

数据库 - 图13

select * 
from com right join region
on com.id = region.id
id name address
1 Google 美国
5 null 中国
3 微博 中国
6 null 美国

full join 并集

从左右表返回所有行,如果其中一个表的数据行在另一个表中没有匹配行,
— 返回NULL

SELECT column_name(s)
FROM table_name1
FULL JOIN table_name2 
ON table_name1.column_name=table_name2.column_name

数据库 - 图14

id name address
1 Google 美国
2 淘宝 null
3 微博 中国
4 Facebook null
5 null 中国
6 null 美国