MySQL注意知识点
INT(N)表示什么?
N是显示宽度,不表示存储数字的长度限制。使用zerofill表示长度小于N时,使用0填充高位,直到长度为N,长度大于N时,按照实际显示。
自动增长注意点
自动增长需要设置在主键上
主键插入为null或者0时,会自动增长
主键插入负数时,会按照实际值插入
字符类型
char(n)和varchar(n) 其中n表示的是字符长度。
其它例如text(n),longtext(n) n表示的是字节
数据库排列规则Collation
设置为utf8_general_ci时,会忽略字符的大小写。
而使用utf8mb4_bin时,_bin结尾的,不会忽略大小写
时间类型
日期类型 | 占用空间 | 表示范围 |
---|---|---|
DATETIME | 8 | 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 |
DATE | 3 | 1000-01-01 ~ 9999-12-31 |
TIMESTAMP | 4 | 1970-01-01 00:00:00UTC ~ 2038-01-19 03:14:07UTC |
YEAR | 1 | YEAR(2):1970-2070, YEAR(4):1901-2155 |
TIME | 3 | -838:59:59 ~ 838:59:59 |
DATETIME 没有时区的概念,而TIMESTAMP有时区的概念
显示MySQL的最大连接数:
show VARIABLES like '%max_connections%'
MySql缓存默认关闭,开启后要命中要求2次SQL要完全一样,包括SQL语句,连接的数据库,协议版本,字符集等。
默认关闭缓存:
show variables like '%query_cache_type%'
在 mysql 中 database 和 schema 是等价的
MySQL存储引擎
Innodb
MySQL5.5以后默认的存储引擎,完全支持事务和ACID特性,支持行级锁。
MyISAM
MySQL5.5之前默认的存储引擎,有myd(数据)和myi(索引)组成
支持表压缩,但是表压缩后,就不能再插入数据了。适用于非事务类型应用,只读应用,空间类应用。基本上大多数场景都不适合了。
CSV
以 csv 格式进行数据存储
所有列都不能为 null 的
不支持索引(不适合大表,不适合在线处理)
可以对数据文件直接编辑(保存文本文件内容)
Archive
以 zlib 对表数据进行压缩,磁盘 I/O 更少,数据存储在 ARZ 为后缀的文件中。
只支持 insert 和 select 操作,只允许在自增 ID 列上加索引
Memory
文件系统存储特点,也称 HEAP 存储引擎,所以数据保存在内存中
支持 HASH 索引和 BTree 索引
所有字段都是固定长度 varchar(10) = char(10)
不支持 Blog 和 Text 等大字段
Memory 存储引擎使用表级锁
最大大小由 max_heap_table_size 参数决定
使用场景
hash 索引用于查找或者是映射表(邮编和地区的对应表)
用于保存数据分析中产生的中间表
用于缓存周期性聚合数据的结果表
Ferderated
提供了访问远程 MySQL 服务器上表的方法
本地不存储数据,数据全部放到远程服务器上
本地需要保存表结构和远程服务器的连接信息
使用场景
偶尔的统计分析及手工查询(某些游戏行业)
MySQL中的锁
表级锁,适合以查询为主,只有少量按索引条件更新数据的应用。会把整个表锁住
行级锁,适合按照索引条件并发更新少量不同数据,同时又有并发查询的应用。
页面锁(gap锁,间隙锁)
表锁
表共享读锁。(读锁)
表独占写锁。(写锁)
读锁
语法:
lock table 表名 read # 加锁 UNLOCK TABLES # 解除锁
多个session可共享读,当前session写操作会报错,其它session写操作会阻塞。当前session查询时,不能设置表别名,否则报错。
写锁
lock table 表名 write # 加锁 UNLOCK TABLES # 解除锁
同一个session中,可以进行增删改操作,其它session的增删改操作会阻塞。所有的读操作都会被阻塞。
行锁
读锁(共享锁)
BEGIN SELECT * FROM 表名 WHERE 条件 LOCK IN SHARE MODE
COMMIT
一个session开启一个事务,设置一个行级读锁,另外一个session就不能对已经上锁的数据进行写操作。比如设置条件为id <=13,那么对id为小于等于13的数据,都不能进行写操作,会阻塞。同个session可以进行操作。
写锁(排它锁)
同一个session中可以进行读写操作,另外一个session就不能对已经上锁的数据进行写操作。比如设置条件为id <=13,那么对id为小于等于13的数据,都不能进行写操作,会阻塞。
注意点
两个事务不能锁同一个索引。
insert ,delete , update 在事务中都会自动默认加上排它锁。
行锁必须有索引才能实现,否则会自动锁全表,那么就不是行锁了。**
事务
事务特性
ACID
原子性atomicity,一个事务为一个不可分割的最小单元,事务中的操作,要么全部成功,要么全部失败。
一致性consistency,事务将数据库从一种一致性转换到另外一种一致性状态,数据库中的数据完整性没有被破坏。
隔离性isolation,一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性durability,事务一旦提交,其所做的修改就会永久保存到数据库中。
事务并发问题
未提交读(READ UNCOMMITED)脏读
已提交读 (READ COMMITED)不可重复读
可重复读(REPEATABLE READ)
可串行化(SERIALIZABLE)
MySQL的默认隔离级别为REPEATABLE-READ
show variables like '%isolation%';
间隙锁(gap锁)
MySQL中,可重复读就已经解决了幻读的问题,借助的就是间隙锁。
事务语法
- 开启事务:
begin
start transaction(推荐)
begin work
事务回滚:rollback
事务提交:commit
还原点:savepoint point名称a。 rollback to savepoint a 回滚到还原点a
业务设计
逻辑设计
范式设计
第一大范式:数据库表中的所有字段都只具有单一属性,单一属性的列是由基本数据类型所构成的。
第二大范式:表中只有一个业务主键。
第三大范式:每一个非主属性既不部分依赖于也不传递依赖于业务主键。
优点:
可以尽量得减少数据冗余
范式化的更新操作比反范式化更快
范式化的表通常比反范式化的表更小
缺点:
对于查询需要对多个表进行关联
更难进行索引优化
反范式设计
为了性能和读取效率考虑,适当违反数据库设计范式要求,允许存在少量冗余。使用空间来换取时间
优点:
可以减少表的关联
可以更好的进行索引优化
缺点:
存在数据冗余及数据维护异常
对数据的修改需要更多的成本
物理设计
命名规范
数据库、表、字段的命名要遵守可读性原则,使用下划线或者大小写来格式化命名以获得良好的可读性
数据库、表、字段的命名要遵守表意性原则,对象的名字应该能够描述它所表示的对象
数据库、表、字段的命名要遵守长名原则 ,尽可能少使用或者不使用缩写
数据类型选择
当一个列可以选择多种数据类型时
优先考虑数字类型
其次是日期、时间类型
最后是字符类型
对于相同级别的数据类型,应该优先选择占用空间小的数据类型
慢查询
分析工具: Mysqldumpslow,pt_query_digest
索引
索引分类
普通索引:一个索引只包含单个列,一个表可以有多个单列索引
唯一索引:索引的值必须唯一,允许有空值
复合索引:一个索引包含多个列
聚簇索引(聚集索引):并不是一种单独的索引类型,而是一种数据存储方式。具体细节取决于不同的实现,InnoDB 的聚簇索引其实就是在同一个结构中保存了 B-Tree 索引(技术上来说是 B+Tree)和数据行。
非聚簇索引:不是聚簇索引,就是非聚簇索引
基础语法
查看索引:SHOW INDEX FROM 表名
创建索引
CREATE [UNIQUE ] INDEX indexName ON 表名(columnName(length));
ALTER TABLE 表名 ADD [UNIQUE ] INDEX [indexName] ON (columnName(length))
删除索引
DROP INDEX [indexName] ON 表名;
执行计划
使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理SQL语句的。分析查询语句或者表结构性能瓶颈
作用
可以查询到以下信息:
表的读取顺序
数据读取操作的操作类型
哪些索引可以使用
哪些索引被实际使用
表之间的引用
每张表有多少行被优化器查询
详解
通过explain关键字分析的结果由以下列组成。
id列
描述 select 查询的序列号,包含一组数字,表示查询中执行 select 子句或操作表的顺序
根据 ID 的数值结果可以分成一下三种情况
id 相同:执行顺序由上至下
id 不同:如果是子查询,id 的序号会递增,id 值越大优先级越高,越先被执行
id 相同和不同:同时存在时,相同id执行顺序由下至上,不同的大的优先执行
select_type列
查询的类型,用于区分普通查询、联合查询、子查询等复杂查询
table列
标识这行数据来源于哪张表
type列
显示的是访问类型,结果值从最好到最坏依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery >
index_subquery > range > index > ALL
关注的结果值:
system>const>eq_ref>ref>range>index>ALL
一般来说,得保证查询至少达到 range 级别,最好能达到 ref。
type列各个值含义
- system:表只有一行记录(等于系统表),这是 const 类型的特列,平时不会出现,这个也
可以忽略不计
- const:表示通过索引一次就找到了
const 用于比较 primary key 或者 unique 索引。因为只匹配一行数据,所以很快如将主键置于 where条件中,MySQL 就能将该查询转换为一个常量
- eq_ref: 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引
扫描
- ref: 非唯一性索引扫描,返回匹配某个单独值的所有行.
本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合
条件的行,所以他应该属于查找和扫描的混合体
range: 只检索给定范围的行,使用一个索引来选择行。key 列显示使用了哪个索引一般就是在你的 where 语句中出现了 between、<、>、in 等的查询
index : 当查询的结果全为索引列的时候,虽然也是全部扫描,但是只查询的索引库,而没有去查询
数据。
- all : 将遍历全表以找到匹配的行
possible_keys 与 key
possible_keys:可能使用的 key
Key:实际使用的索引。如果为 NULL,则没有使用索引
查询中若使用了覆盖索引,则该索引和查询的 select 字段重叠
key_len
表示索引中使用的字节数,可以通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好
key_len 显示的值为索引字段的最大可能长度,并非实际使用长度,即 key_len 是根据表定义计算而得,不是通过表内检索出的
key_len 表示索引使用的字节数,
根据这个值,就可以判断索引使用情况,特别是在组合索引的时候,判断所有的索引字段是否都被查询用到。
char 和 varchar 跟字符编码也有密切的联系,
-
latin1占用 1 个字节,gbk 占用 2 个字节,utf8 占用3 个字节。(不同字符编码占用的存储空间不同)
-
字符类型-索引字段为char类型+不可为Null时
如果索引定义为char(20) ,则key_len= 20 * 3(utf-8b编码)
-
字符类型-索引字段为char类型+可为Null时
如果索引定义为char(20) ,则key_len= 20 * 3(utf-8b编码)+ 1 = 31
可以为Null,占用一个字节长度,需要加1
-
索引字段为varchar类型+不可为Null时
Keylen=varchar(n)变长字段+不允许 Null=n*(utf8=3,gbk=2,latin1=1)+2
-
索引字段为varchar类型+可为Null时
Keylen=varchar(n)变长字段+不允许 Null=n*(utf8=3,gbk=2,latin1=1)+2+1
总结
字符类型:
变长字段需要额外的 2 个字节(VARCHAR 值保存时只保存需要的字符数,另加一个字节来记录长度(如果列声明的长度超过 255,则使用两个字节),所以 VARCAHR 索引长度计算时候要加 2),固定长度字段不需要额外的字节。
NULL 需要 1 个字节的额外空间,所以索引字段最好不要为 NULL,因为 NULL 让统计更加复杂并且需要额外的存储空间。
复合索引有最左前缀的特性,如果复合索引能全部使用上,则是复合索引字段的索引长度之 和,这也可以用来判定复合索引是否部分使用,还是全部使用。
整数/浮点数/时间类型的索引长度
NOT NULL=字段本身的字段长度
NULL=字段本身的字段长度+1(因为需要有是否为空的标记,这个标记需要占用 1
个字节)
- datetime 类型在 5.6 中字段长度是 5 个字节,datetime 类型在 5.5 中字段长度是 8 个字节
ref
显示索引的哪一列被用到了
rows
根据表统计信息及索引选用情况,大致估算出找到所需的记录需要读取的行数。不准确。
extra
包含不适合在其它列中展示但十分重要的额外信息。
using filesort
当发现using filesort时,就是有优化的地方了。
比如联合索引是a,b,c三个字段,查询时却是只按照c排序,就会导致索引失效。
using Index
表名使用了覆盖索引,避免了访问表的数据行,效率高。
覆盖索引
select 的数据列只用从索引中就能够取得,不必读取数据行,MySQL 可以利用索引返回 select 列表中的字段,而不必根据索引再次读取数据文件,换句话说查询列要被所建的索引覆盖。
如果要使用覆盖索引,一定要注意 select 列表中只取出需要的列,不可 select *,因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降。
SQL优化
优化策略
尽量全值匹配
最佳左前缀法则(复合索引)。查询从索引的最左前列开始,并且不跳过索引中的列。
不在索引上做任何操作。包括计算、函数、类型转换。会导致索引失效而全表扫描
范围条件放最后。中间有范围查询会导致后面的索引列全部失效。
覆盖索引尽量用。
不等于要慎用。使用不等于会导致索引失效而全表扫描。需要使用不等于时,使用覆盖索引。
Null/not null有影响。 查询is not null 时,会导致索引失效。查询is null时,如果索引是not null的,就会导致索引失效,如果不是,索引有效。解决方案:覆盖索引
like查询要当心。like 以通配符开头('%abc...')索引失效会变成全表扫描的操作。
字符类型加引号。字符串不加单引号会导致索引失效。
or改成union效率高。
导入导出数据
load data infile
select * into OUTFILE 'D:\product.txt' from product_info
load data INFILE 'D:\product.txt' into table product_info
load data INFILE '/soft/product3.txt' into table product_info
show VARIABLES like 'secure_file_priv'
secure_file_priv 为 NULL 时,表示限制 mysqld 不允许导入或导出。
secure_file_priv 为 /tmp 时,表示限制 mysqld 只能在/tmp 目录中执行导入导出,其他目录不能执行。
secure_file_priv 没有值时,表示不限制 mysqld 在任意目录的导入导出。
-