MySQL学习笔记

MySQL数据库学习

基础篇

关于MySQL的安装和一些基本语法

概述

关系型数据库

建立在关系模型基础上,由多张相互连接的二维表组成的数据库
特点: 使用表存储数据,格式统一,便于维护;使用SQL语言操作,标准统一,使用方便

数据模型

SQL语法

通用语法

  • SQL语句可以单行书写也可以多行书写,以分号结尾
  • SQL语句可以使用空格/缩进来增强语句的可读性
  • MySQL数据库的SQL语句不区分大小写,关键字建议用大写
  • 注释:
    单行注释:--注释内容或#注释内容
    多行注释:/*注释内容*/

SQL分类

  • DDL(Data Definition Language):数据定义语言,用来定义数据库对象(数据库,表,字段)
  • DML(Data Manipulation Language):数据操作语言,用来对数据库表中的数据进行增删改
  • DQL(Data Query Language):数据查询语言
  • DCL(Data Control Language):数据控制语言,用来创建数据库用户,控制数据库的访问权限

DDL

数据库操作

查询数据库

SHOW DATABASES;

查询当前数据库

SELECT DATABASE();

创建数据库

CREATE DATABASE [IF NOT EXISTS] 数据库名 [DEFAULT CHARSET 字符集] [COLLATE 排序规则];

删除数据库

DROP DATABASE [IF EXISTS] 数据库名;

使用数据库

USE 数据库名;

表操作

查询当前数据库所有表

SHOW TABLES;

查询表结构:显示表的字段信息

DESC 表名;

查询指定表的建表语句

SHOW CREATE TABLE 表名;

创建表

CREATE TABLE 表名(字段1 数据类型 [COMMENT 注释],...)[COMMENT 表注释];

添加字段

ALTER TABLE tb_name ADD 字段 数据类型 [COMMENT 注释];

修改字段:需要保证已有数据与新类型兼容

ALTER TABLE tb_name MODIFY 字段名 新数据类型; #修改字段数据类型
ALTER TBALE tb_name CHANGE 旧字段名 新字段名 数据类型 [COMMENT 注释] [约束];--重命名字段

删除字段

ALTER TABLE tb_name DROP 字段名;

修改表名

ALTER TABLE tb_name RENAME TO new_tb_name;

删除表

DROP TABLE IF EXISTS tb_name;--删除整个表
TRUNCATE TABLE tb_name; #删除数据保留表的格式

数据类型

整数

数据类型 字节数 取值范围
TINYINT 1 [0-255]
SMALLINT 2 [0-65535]
MEDIUMINT 3 [0-16777215]
INT 4 [0-4294967295]
BIGINT 8 [0-18446744073709551616]

int(5) 形式用来表示显示的位宽,不足的用0填,超过显示全部数据,此形式只有在数据类型设置了unsigned zerofill才有效,数据占用空间仍为4个字节
浮点型

数据类型 字节数 描述
float 4 单精度浮点数
double 8 双精度浮点数

float(M,D) 表示数据位一共是M位,D位小数位

定点型

decimal(M,D) 浮点型容易出现精度丢失问题,decimal 常用于银行系统,互联网金融系统

浮点型是以近似值存储,定点型是以字符串形式进行保存
日期类型

数据类型 字节数 格式 备注
date 3 yyyy-MM-dd 存储日期值
time 3 HH:mm:ss 存储时分秒
year 1 yyyy 存储年
datetime 8 yyyy-MM-dd HH:mm:ss 存储完整日期
timestamp 4 yyyy-MM-dd HH;mm:ss 存储完整时间,可作时间戳

datetime与timestamp的区别

datetime范围为1000-01-01 00:00:00 到 9999-12-31 23:59:59

timestamp范围为1970-01-01 08:00:01 到 2038-01-19 11:14:07

datetime默认值可以为null,timestamp默认值为当前时间,timestamp存储的时间与当前时区相关

字符串

数据类型 字符数 描述
CHAR(N) 0-255 bytes 定长字符串
VARCHAR(N) 0-65535 byres 变长字符串
TINYBLOB 0-255 bytes 不超过255字符的二进制字符串
TINYTEXT 0-255 bytes 段文本字符串
BLOB 0-65535 bytes 二进制形式的长文本数据
TEXT 0-65535 bytes 长文本数据
MEDIUMBLOB 0-16777215 bytes 二进制形式的中等长度文本数据
MEDIUMTEXT 0-16777215 bytes 中等长度文本数据
LONGBLOB 0-4294967295 bytes 二进制形式的极大文本数据
LONGTEXT 0-4294967295 bytes 极大文本数据

char(n)是固定长度,不管存入几个字符,都占用n个字节,varchar(n)是变长字符串,varchar是存入的实际字符数+1个字节数据(n>255时是+2),char类型的字符串检索速度比varchar类型快。char存储的字符数小于n时,后面补空格,检索时再删除所有空格,所以char字符串不能包含空格,varchar则不受限。

DML

添加数据

INSERT INTO tb_name(字段名1,字段名2,...) VALUES(值1,值2,...);

给表中所有字段添加数据

INSERT INTO tb_name VALUES(值1,值2,...);

批量添加

INSERT INTO tb_name(字段名1,字段名2,...) VALUES(值1,值2,...),(值1,值2,...);

字符串和日期应用引号括起来

修改数据

UPDATE 表名 SET 字段名1 = 值1,... [WHERE 条件];

删除数据

DELETE FROM tb_name [WHERE 条件];

DQL

查询指定字段并起别名

SELECT 字段名 AS  别名 FROM 表名;

条件查询

between ... and 是闭区间,in是在其中之一就可以

聚合函数:将一列数据作为一个整体,纵向计算

函数 功能
count 统计数量
max 最大值
min 最小值
avg 平均值
sum 求和

语法

SELECT 聚合函数(字段列表) FROM 表名;

分组查询

SELECT 字段列表 FROM 表名 [WHERE 条件] GROUP BY 分组字段名 HAVING 分组后过滤条件;

where和having的区别

执行时机不同:where是分组前过滤,不满足where不进行分组,而having是分组后进行过滤

判断条件不同:where不能对聚合函数进行判断,而having可以

执行顺序:where>聚合函数>having

分组之后查询的字段一般是聚合函数和分组字段,查询其他字段无任何意义
排序查询

SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式,字段2 排序方式;

ASC 升序 DESC 降序
分页查询

SELECT 字段列表 FROM 表名 LIMIT 起始索引,查询记录数;

起始索引=(查询页码-1)x每页记录数,起始索引从0开始
执行顺序

FROM 表 WHERE 条件列表 GROUP BY 分组列表 HAVING 条件列表 SELECT 字段列表 ORDER BY 排序列表 LIMIT 分页参数;

DCL:管理用户

查询用户

USE mysql;
SELECT * FROM USER;

创建用户

CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';

修改用户密码

ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql-native-passwd BY '新密码';

删除用户

DROP USER '用户名'@'主机名';

主机名可以使用%通配

查询权限

SHOW GRANTS FOR '用户名'@'主机名';

授予权限

GRANT 权限 ON 库名.表名 TO '用户名'@'主机名';

撤销权限

REVOKE 权限列表 ON 库.表 FROM '用户名'@'主机名' ;

函数

字符串函数

名称 功能
concat(S1,S2,...) 拼接字符串
lower(str) 小写
upper(str) 大写
lpad(str,n,pad) 左侧填充pad字符到n长度
rpad(str,n,pad) 右侧填充
trim(str) 去掉首尾空格
substring(str,start,len) 返回len长度子串

数值函数

名称 功能
ceil(x) 向上取整
floor(x) 向下取整
mod(x,y) 取x/y的余数
rand() 0-1之间随机数
Round(x,y) x四舍五入保留y位小数

日期函数

名称 功能
curdate() 返回当前日期
now() 返回当前日期和时间
curtime() 返回当前时间
year(date) 返回date对应的年份
date_add(date,INTERVAL expr type) 返回date间隔expr后的时间
datediff(date1,date2) 返回两个日期间隔的天数

流程函数

名称 功能
IF(value,t,f) 如果alue为true,返回t,否则返回f
IFNULL(value1,value2) value1不为空返回,否则返回value2
CASE WHEN [value1] THEN [res1] ELSE [default] END 如果value1为真,返回res1,否则default
CASE [expr] WHEN [value1] THEN [res1] ELSE [default] END 如果expr的值为val1,返回res1,否则返回default

约束:作用于表中字段上的规则,用于限制存储在表中的数据

类型 含义 表示
非空约束 不为空 not null
唯一约束 唯一 unique
主键约束 唯一且不为空 primary key
默认约束 未指定值时采用默认值 default
外键约束 两表间建立连接(MyISAM不支持) foreign key或者MUL
检查约束 检查字段是否符合约束条件 check

外键约束:保证数据间的一致性和完整性,不允许当前表插入一条外键值不存在的记录
语法

CREATE TABLE tb_name(...,[CONSTRAINT] [外键名称] FOREIGN KEY (外键字段) REFERENCES 主表(主表列名));

或者

ALTER TABLE tb_name ADD CONSTAINT 外键名称  FOREIGN KEY (外键字段) REFERENCES 主表(主表列名);

删除外键

ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;

删除更新行为:主表删除记录时是否需要检查和更新子表

名称 解释
NO ACTION 删除更新主表时,查看该记录是否有对应外键,有则不允许删除更新
RESTRICT 同NO ACTION
CASCADE 删除更新时,一并删除更新子表记录
SET NULL 删除时子表更新为NULL
SET DEFAULT 主表更新时,子表更新为默认值(InnoDB不支持)

语法

ALTER TABLE ... ON UPDATE CASCADE ON DELETE CASCADE;

多表查询

多表关系

关系 处理方式
一对多(多对一) 在多的一方建立外键,指向一的主键
多对多 建立第三张中间表
一对一 多用于单表拆分,以提升操作效率,在任意一方加入主键,关联另一方的主键,并且设置外键为唯一的

多表查询

笛卡尔积,需要消除无效的笛卡尔积

内连接查询

查询两表的交集
隐式内连接查询

SELECT 字段 FROM 表1,表2 WHERE 条件;

显式内连接查询

SELECT 字段 FROM 表1 [inner] join 表2 ON 连接条件;

注:显式内连接比隐式内连接性能要好

外连接查询

左表或者右表的全部,加上两表的交集,交集为空则字段值默认为NULL
左连接查询

显示左表的全部

SELECT 字段 FROM 表1 LEFT [OUTER] JOIN 表2 ON 条件;

右连接查询

显示右表的全部

SELECT 字段 FROM 表1 RIGHT [OUTER] JOIN 表2 ON 条件;
自连接查询

当前表与自己建立连接查询,可以是内连接也可以是外连接查询,必须要取别名

SELECT 字段列表 FROM 表1 别名A JOIN 表2 别名2 ON 条件;
联合查询

把多次查询结果合并起来,形成新的结果集

SELECT 字段列表 FROM 表A 
UNION [ALL]
SELECT 字段列表 FROM 表B;

对于联合查询,多张表的列数和字段类型要保持一致,UNION ALL不去重

子查询

又叫做嵌套查询,是指在一个SQL查询中嵌套另一个查询,子查询可以放在SELECT,FROM,WHERE,HAVING等语句中,用于从另一个查询结果集中检索数据
标量子查询

子查询返回单个值

SELECT 字段列表 FROM 表名 WHERE 字段 = (SELECT 字段 FROM 表名 WHERE 条件);

列子查询

子查询返回的结果是一列

SELECT 字段列表 FROM 表名 WHERE 字段 = (SELECT 字段 FROM 表名 WHERE 条件);

行子查询

子查询返回的结果是一行

SELECT 字段列表 FROM 表名 WHERE 字段 = (SELECT 字段列表 FROM 表名 WHERE 条件);

表子查询

子查询返回的结果是多行多列

事务

一组操作的集合,是一个不可分割的工作单位,事务会把所有操作作为整体向系统提交或撤销操作,即这些操作要么同时成功要么同时失败
语法

SELECT @@autocommit; # 设置事务提交方式
COMMIT; # 提交事务
ROLLBACK; # 回滚事务
START TRANSACTION; # 开始事务 BEGIN也可以

ACID特性

原子性(Atomicity):事务是不可分割的原子单元,要么全部成功,要么全部失败

一致性(Consistency):事务完成时,必须使所有的数据保持一致的状态

隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行

持久性(Durability):事务一旦提交或回滚,则对数据的改变是永久的

并发事务问题

问题 原因
脏读 一个事务读到另一个事务还没提交的数据
不可重复读 一个事务先后读取同一条记录,但两次读取的数据不同
幻读 一个事务按条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在了,好像出现了幻影

解决方案

事务隔离级别 脏读 不可重复读 幻读
Read Uncommitted
Read Committed
Repeatable Read
Serializable

查看事务隔离级别

Mysql 复制代码
SELECT @@TRANSACTION_ISOLATION

设置事务隔离级别

Mysql 复制代码
SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE];

[注]:事务隔离级别越高,数据越安全,但性能越低

进阶篇

存储引擎

存储数据,建立索引,更新、查询数据等技术的实现方式,存储引擎是基于表的,不是基于库的

InnoDB

兼顾高可靠性和高性能的通用存储引擎,MySQL5.5后,默认的存储引擎
特点

DML操作遵循ACID模型,支持事务

行级锁,提高并发访问性能

支持外键约束,保证数据的完整性和正确性
文件: xxx.ibd文件,InnoDB引擎的每张表都会对应这样的一个表空间文件,存储该表的表结构(frm,sdi),数据和索引
参数: innodb_file_per_table
InnoDB逻辑存储结构

MyISAM

MySQL早期的默认存储引擎
特点: 不支持事务,不支持外键;支持表锁,不支持行锁,访问速度快
文件: sdi文件,存储表结构信息,MYD文件,存储数据,MYI文件,存储索引

Memory

表数据存储在内存中,只能做临时表或缓存使用
特点: 内存存放,hash索引
文件: sdi文件,存储表结构信息

存储引擎选择

InnoDB:应用对事务的完整性有比较高的要求,除插入和查询外,包含很多更新,删除操作
MyISAM:以读和插入为主,只有很少的更新和删除操作,对事务和并发要求不高
Memory:用作临时表和缓存

索引

帮助MySQL高效获取数据的数据结构(有序)
优点

提高数据检索的效率,降低数据库的IO成本

通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗
缺点

索引列占用空间,提高查询效率,降低更新效率

结构

B+Tree索引

B Tree(多路平衡查找树)

以m路搜索树为例,分支节点包含数据,除根节点外 (至少包含一个key),每个节点包含:m/2 -1(向上取整)<=i<=m-1个key,并且所有的叶子节点都在同一层
B+Tree

B Tree相比,B+Tree 做了如下改变,一是分支节点只有key,不保存数据,叶子节点保存所有的数据,二是叶子节点形成一个单向链表

MySQL 对经典的B+Tree做了优化,在原B+ Tree的基础上,增加了一个指向相邻叶子节点的链表指针,形成了带有顺序指针的B+Tree,提高了区间访问的性能

Hash索引

采用一定的Hash算法,将键值换算成新的Hash值,映射到对应的槽位上,然后存储在Hash表中,产生Hash碰撞时,可以通过链表解决
特点

Hash索引只能用于对等比较(=,in),不支持范围查找,并且无法排序,查询效率高于B+Tree
存储引擎支持

Memory引擎支持Hash索引,InnoDB引擎中具有自适应Hash功能,Hash索引是 存储引擎根据B+Tree索引在指定条件下自动构建的

分类

分类 含义 特点 关键字
主键索引 针对表中主键创建的索引 默认自动创建,只能有一个 PRIMARY
唯一索引 避免同一个表中某数据列的值重复,NULL值可以重复 可以有多个 UNIQUE
常规索引 快速定位特定数据 可以有多个
全文索引 查找文本中的关键字,不是比较索引的值 可以有多个,MyISAM支持 FULLTEXT

根据索引存储形式分类

分类 含义 特点
聚集索引 数据与索引存放在一起,索引的叶子节点保存在行数据中 有且仅有一个
二级索引 数据和索引分开存储,叶节点关联对应的主键 可以有多个

聚集索引选取规则

如果存在主键,主键索引就是聚集索引

如果不存在主键,将使用第一个唯一索引作为聚集索引

否则,InnoDB会自动生成一个rowId作为隐藏的聚集索引

语法

创建索引

msyql 复制代码
CREATE [UNIQUE|FULLLTEXT] INDEX index_name ON tb_name (col1,...);

mysql 复制代码
CREATE TABLE tb_name(字段1 数据类型,...,
[UNIQUE|FULLTEXT] INDEX [index_name](col1 desc,col2 asc,...)
);

mysql 复制代码
ALTER TABLE tb_name ADD [UNIQUE|FULLTEXT] INDEX [index_name](col1 asc,col2 desc,...);

查看索引

mysql 复制代码
SHOW INDEX FROM tb_name;

删除索引

mysql 复制代码
DROP INDEX index_name ON tb_name;

mysql 复制代码
ALTER TABLE tb_name DROP INDEX index_name;

索引使用

最左前缀法则

联合索引遵循最左前缀法则,查询从索引的最左列开始,并且不跳过索引中的列
范围查询

联合索引中,出现范围查询(>,<),范围查询的右侧的列索引失效,尽量使用(>=,<=)符号,此时右侧索引列不是失效
索引列运算

不要在索引列进行运算操作,索引将失效
字符串不加引号,索引将失效
模糊查询

头部模糊查询索引将失效
or连接

用or分隔开的条件,只有前后均含索引时,才会使用索引
数据分布影响

如果MySQL评估使用索引比全表查询更慢,则不适用索引
SQL提示

优化数据库的一个重要手段,在SQL中加入一些人为的提示来达到优化操作的目的

SELECT * FROM TB_USER FORCE/IGNORE/USE INDEX(INDEX_NAME) WHERE 条件;

覆盖索引

减少使用SELECT *,多使用覆盖索引,覆盖索引是SELECT的数据只用从索引中就能取得
前缀索引

当字段类型是字符串(varchar,text等)时,可以只将字符串中的一部分前缀建立索引,可以大大节约索引时间,从而提高索引效率
前缀长度 可以根据索引的选择性来决定,选择性是指不重复的索引值和数据表的记录总数的比值,选择性越高则查询效率越高

SELECT count(distinct substring(字段,1,5))/count(*) FROM TB_USER;

单列索引和联合索引

业务场景中,如果存在多个查询条件,考虑针对查询字段建立索引时,建议建立联合索引

索引设计原则

  1. 针对数据量比较大,且查询比较频繁的表建立索引
  2. 针对常作为查询条件,排序,分组操作的字段建立索引
  3. 尽量选择区分度高的列建立索引,尽量建立唯一索引
  4. 字符串较长时,可以建立前缀索引
  5. 尽量使用联合索引,减少单列索引
  6. 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束条件

SQL性能分析

SQL语句执行频率

MySQL 复制代码
SHOW GLOBAL STATUE LIKE 'COM_______';

慢日志查询

慢日志记录了所有执行时间超过指定参数的(long_query_time,单位:秒,默认取值10秒)的所有SQL语句的日志,MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/mysql/my.cnf)中配置如下信息slow_query_log=1,long_query_time=2

配置完毕以后,通过以下指令重新启动MySQL服务器进行测试,查看慢日志文件中记录的信息/var/lib/mysql/localhost-slow.log

systemctl restart mysql/mysqld

profile详情

查看是否支持profile操作

mysql 复制代码
SELECT @@HAVE_PROFILING;

开启profiling,默认情况下是关闭的

mysql 复制代码
SET PROFILING=1

查看每条SQL的耗时情况

SHOW PROFILES;

查看指定query_id的SQL语句各个阶段的耗时情况

mysql 复制代码
SHOW PROFILE FOR QUERY query_id;

查看指定query_id的SQL语句CPU的使用情况

MYSQL 复制代码
SHOW PROFILE CPU FOR QUERY query_id;

explain执行计划

EXPLAIN或DESC命令获取MySQL如何执行SELECT语句的信息

字段 含义
id 序列号,id相同,执行顺序从上往下,id不同,值越大,越先执行
select_type SIMPLE(简单表,不适用表连接或子查询),PRIMARY(主查询,即外层的查询),UNION(UNION中的第一个或者后面的查询语句),SUBQUERY(包含子查询)
type 连接类型,由好到坏:null(不访问任何表),system(访问系统表),const(主键查询或者唯一索引查询),eq_ref,ref(非唯一索引查询),range,index(使用了索引,但是会扫描索引),all(全表扫描)
possible_key 显示可能应用到这张表上的索引,一个或多个
key 实际用到的索引,NULL表示没用索引
key_len 表示索引中使用的字节数,为索引用到的最大可能长度,并非实际使用长度,在不损失精度的前提下,越小越好
rows 估计必须要查询的行数
filtered 返回结果的行数占需读取行数的百分比,值越大越好

SQL优化

插入数据-insert优化
  • 批量插入
mysql 复制代码
INSERT INTO tb_user values(),(),(),,,;
  • 手动提交事务 系统默认自动提交事务,每一条insert语句都开启一个事务,频繁提交事务会影响性能
MYSQL 复制代码
START TRANSACTION;...;COMMIT;
  • 主键顺序插入 主键顺序插入性能高于乱序插入
  • 大批量数据插入 如果一次性插入大量数据,使用insert性能太低,使用load指令插入
mysql 复制代码
# 客户端连接服务器端时,加上参数 --local-infile
mysql --local-infile -u user_name - p passwd 
# 设置全局参数local_infile=1,开启从本地加载文件导入数据的开关
set global local_infile=1;
# 执行load命令,将数据加载到表结构中
load data local infile '文件路径' into table `tb_user` fields terminated by '分隔符' lines terminated by '分隔符';
主键优化

数据组织形式:在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,称为索引组织表(Index Organizied Table)
页分裂:页可以为空,也可以填充一半,也可以100%,每个页包含了2-N行数据,根据主键排列,主键乱序插入时,若插入的数据在两页之间,InnoDB会找到前一页中间靠后的位置进行分裂,将后半页与新数据存储到新的一页
页合并:删除一行记录时,记录没有被物理删除,只是被标记为删除且它的空间边的允许被其他记录声明使用,当页中删除的记录达到MERGE_THRESHOLD(默认50%),InnoDB会开始寻找最靠近的页,看看是否可以优化空间使用
主键设计原则

  • 满足业务需求的情况下, 尽量降低主键的长度
  • 插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键
  • 尽量不要使用UUID或其他自然主键(如身份证号)
  • 业务操作时,避免对主键的修改
order优化

Using filesort: 查询满足条件的数据行,然后在排序缓冲区sort buff中完成排序操作
Using Index: 通过有序索引顺序扫描直接返回有序数据,不需要额外的排序,操作效率高

  • 根据排序字段建立合适的索引,多字段排序,也遵循最左前缀法则
  • 尽量使用覆盖索引,覆盖索引不需要再回表查询,在使用覆盖索引的前提下才会有可能是Using Index
  • 多字段排序,一个升序一个降序时,需要注意联合索引在创建时的规则
  • 如果不可避免的出现filesort,大数据量排序时,可以适当增加排序缓冲区大小sort_buffer_size(默认是256K),以避免出现磁盘文件排序
group by优化
  • 分组操作时,可以通过索引来提高效率
  • 分组操作时,索引的使用也满足最左前缀法则
limit优化

一个常见又头疼的问题是limit 2000000,10,此时需要Mysql排序前2000010条数据,再返回2000000-2000010条数据,其他记录丢弃,查询排序的代价非常大

  • 一般分页查询时,通过创建覆盖索引能较好地提高性能,可以通过覆盖索引加子查询进行优化
mysql 复制代码
SELECT s.* from tb_user as s,(SELECT id from tb_user order by id limit 2000000,10) t where s.id=t.id;
count优化

count的几种用法
count(主键):InnoDB引擎会遍历整张表,取键值,按主键计数
count(字段):存在not null约束时,按行累加;不存在not null约束时,判断字段是否为空
count(1):遍历整张表,不取值,按行累加
count(*):按行累加,可以衍生为count('test')count(0)等形式

效率排序:count(*)~count(1)>count(主键)>count(字段) 尽量使用count(*)

update优化
  • InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁变成表锁

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

分类

全局锁: 锁住数据库中的所有表,可以读但不能写
表级锁: 锁住整张表
行级锁: 锁住一行数据

全局锁

对整个数据库实例加锁,加锁后整个实例就处于只读状态,使用场景是做全库的逻辑备份,对所有的表进行锁定
特点

  • 如果在主库上备份,备份期间不能执行更新,业务基本停摆
  • 如果在从库上备份,则从库不能执行主库同步过来的二进制日志,会导致主从延迟
表级锁

每次操作锁住整张表,锁定粒度大,并发度最低
分类

表锁,元数据锁,意向锁

表锁

分类

共享读锁(read lock):当前会话只能进行DQL操作,不能进行DML和DDL操作

独占写锁(write lock):当前会话可以进行DQL,DML和DDL操作
语法

mysql 复制代码
lock tables tb_name read/write;
mysql 复制代码
unlock tables/客户端断开连接
元数据锁(MDL)

MDL加锁过程是系统自动控制的,无需显式使用,在访问一张表的时候会自动加上。主要作用是维护表元数据的数据一致性,避免DML和DDL语句的冲突。

实操过程中,如果开启两个事务,一个事务先执行DDL语句,另一个事务后执行DML语句,则DDL不会加上EXCLUSIVE锁,DML也不会加上SHARE_READ锁。

如果开启两个事务,一个事务先执行DML语句,另一个事务后执行DDL语句,则DDL会加上EXCLUSIVE锁,DML会加上SHARE_READ锁。
这表明系统会自动判断需不需要加元数据锁,尽量保持存储引擎的最大并发性能。

意向锁

为了避免DML在执行时,加的行锁和表锁的冲突,在InnoDB中引入意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。
意向共享锁: 与表锁共享锁(read)兼容,与表锁排他锁(write)互斥
意向排他锁: 与表锁共享锁(read)和表锁排他锁(write)互斥。意向锁之间不会互斥

行级锁

行锁

锁定单个行记录的锁,防止其他事物对此进行update和delete,在RC和RR隔离级别下都支持
InnoDB的行锁是针对索引加的锁,不通过索引条件检索数据,那么会升级为表锁
间隙锁

锁定索引记录间隙,确保索引记录间隙不变,防止其他事务在这个间隙进行insert,防止幻读,在RR隔离级别下支持
临键锁

行锁和间隙锁的结合,在RR隔离级别下支持

默认情况下,InnoDB在Repeatable Read事务隔离级别运行,InnoDB使用next-key锁进行搜索和索引扫描,以防止幻读

  • 索引上的等值查询(唯一索引),给不存在的记录加锁时,优化为间隙锁
  • 索引上的等值查询(普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock退化为间隙锁
  • 索引上的范围查询(唯一索引),会访问到不满足条件的第一个值为止

【注】间隙锁可以共存。行锁锁的是主键

MVCC(Multi-Version Concurrency Control)

维护一个数据的多个版本,使得读写操作没有冲突,快照读为MySQL实现MVCC提供了一个非 阻塞读功能,MVCC的具体实现还需要依赖于数据库中的三个隐藏字段,undo log日志,readview
当前读

读取的是记录的最新版本, 读取时还要保证其他并发事务不能修改当前记录,会对读取的记录加锁,lock in share mode,for update,insert into,update,delete都是当前读
快照读

简单的select就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读

事务隔离级别 读类型
Read Committed 每次select都生成一个快照读
Repeatable Read 开启事务后的第一个select语句是快照读的地方
Serializable 快照读退化为当前读
实现原理
数据隐藏字段 含义
DB_TRX_ID 最近修改事务的ID,记录插入这条数据或最后一次修改该记录的事务ID
DB_ROLL_PTR 回滚指针,指向这条记录的上一个版本,用于配合undo log
DB_ROW_ID 隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段

undo log

回滚日志,当insert的时候,产生的undo log只在回滚时需要,在事务提交后,可立即被删除,而update,delete的时候,产生的undo log 日志不仅在回滚时需要,在快照时也需要,不能立即删除(因为insert时产生的是新的数据,没有旧的版本,而delete和update都会有版本链,会指向旧的数据,所以不能删除)
undo log版本链

不同事务或者相同事务对同一条记录进行修改,会导致该记录的undo log生成一条记录版本链表,链表的头部是最新的旧记录,链表的尾部是最早的旧记录。
ReadView

快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id

包含四个核心字段,如下表所示

字段 含义
m_ids 当前活跃的事务ID集合
min_trx_id 最小活跃事务ID
max_trx_id 预分配事务ID,当前最大事务ID+1
creator_trx_id ReadView创建者的事务ID

版本链数据访问规则

  1. trx_id == creator_trx_id 可以访问该版本,说明数据是当前事务修改的
  2. trx_id < min_trx_id:可以访问该版本,说明数据已经提交
  3. trx_id > max_trx_id:不可以访问该版本,说明该事物在ReadView生成后开启
  4. min_trx_id <= trx_id <= max_trx_id && trx_id not in (m_ids):如果trx_id不在活跃事务集合里,说明事务已经提交,可以访问

总结:可以访问ReadView之前已经提交的版本,也可以访问当前事务修改的版本

不同的隔离级别,生成ReadView的时机不同:
READ COMMITTED: 事务每一次执行快照时生成ReadView
REPEATABLE READ: 在事务第一次执行快照读时生成ReadView,后续复用

MySQL刷题

  1. MySQL使用三值逻辑,True,False,Unknown,任何与NULL 进行比较得出的结果都会是Uknown。因此MySQL中提供is nullis not null来判断是否为空。
  2. MySQL中用来计算字符串中字符数的最佳函数是char_length(str),它返回字符串长度,另一个常用的函数LENGTH(str)可能会返回不同的结果,因为该函数返回字符串 str 的字节数,某些字符包含多于 1 个字节,如"¥"。
相关推荐
dayouziei2 小时前
java的类加载机制的学习
java·学习
Yaml44 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
追风林4 小时前
mac 本地docker-mysql主从复制部署
mysql·macos·docker
aloha_7894 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
dsywws5 小时前
Linux学习笔记之vim入门
linux·笔记·学习
晨曦_子画6 小时前
3种最难学习和最容易学习的 3 种编程语言
学习
Hsu_kk6 小时前
MySQL 批量删除海量数据的几种方法
数据库·mysql
编程学无止境6 小时前
第02章 MySQL环境搭建
数据库·mysql
城南vision6 小时前
Docker学习—Docker核心概念总结
java·学习·docker
knight-n6 小时前
MYSQL库的操作
数据库·mysql