深入浅出MySQL备忘录

此文章为《深入浅出MySQL》第二版的备忘录。

第2章 SQL基础

SQL语句分为:

  • DDL(Data Definition Languages)语句: 数据库结构定义为语言。
  • DML(Data Manipulation Languages)语句: 数据操作语句。
  • DCL(Data Control Languages)语句: 权限控制语句。

MySQL自带数据库:

  • information_schema: 存储数据库对象信息,如用户表,列,权限,字符集,分区等信息。此为虚拟数据库,其中的表均为视图。

    • SCHEMATA: 当前mysql实例的所有数据库信息,show databases信息来源。
    • TABLES: 关于数据库的表(视图)信息,show table from ${schame_name}信息来源。
    • COLUMNS: show columns from ${schema_name}.${table_name}信息来源。
    • STATISTICS: 表的索引信息。show index from ${schema_name}.${table_name}信息来源。
  • cluster: 存储集羣信息。
  • mysql: 存储用户权限信息。
  • test: 系统自动创建的测试数据库,任何用户均可使用。

在MySQL世界,drop语句操作的结果都显示 0 rows affected。

WITH ROLLUP可对分类聚合后的结果进行再汇总。
UNION于UNION ALL的区别是会进行一次DISTINCT操作。
? contents 或者 ? <key_words> 可快速查询使用手册。

第3章 数据类型

若列被指定了zerofill,则会自动添加UNSIGNED属性。
DECIMAL默认为DECIMAL(10, 0)

TIMESTAMP:

  1. 表中的第一个TIMESTAMP列的默认值将被设置为当前时间。
  2. 其值的存储与读取与时区有关。

VARCHAR会保留存储后端空格,CHAR不会。

第4章 运算符

<=>: NULL安全的等于, 也即:

  • SELECT NULL <=> NULL return 1
  • SELECT NULL = NULL return 0

第5章 常用函数

第7章 存储引擎

MySQL默认引擎在5.5后更改为InnoDB。

第8章 合适的数据类型

对于InnoDB,内部行存储合适未区分可变长度和固定长度。故推荐使用VARCHAR,其平均空间占用更小。

编程与存储时,精确计算需使用定点数(Decimal,Java中也有此类),浮点数会有误差。
对于BLOB和TEXT,删除和修改操作后均需使用OPTIMIZE TABLE对表进行碎片整理。

第9章 字符集

字符集选择原则

  1. 多语言支持或主要处理英文字符,UTF8。
  2. 只需要支持中文,GBK,因其较小,每个中文占用2个字节,UTF8为3个。
  3. 需要做大量的字符计算,排序,比较等,建议定长字符。
  4. 考虑客户端的字符集。

show character set或查看information_schema.character_set显示所有的字符集(MySQL字符串的存储方式)和该字符集对应的校对规则(比较字符串的方式),其为一对多的关系。
show collation like *或者查看information_schema.COLLATIONS查看相关字符集的校对规则。
校对规则结尾命名:

  1. _ic: 大小写不敏感。
  2. _cs: 大小写敏感。
  3. _bin: 比较字符编码值。

show variables like ‘character_set_${scope}’;
show variables like ‘collation_${scope}’;
查看当前scope(server: 服务器, database: 数据库)的字符集和校对规则。

第10章 索引

索引列的数据越分散,筛选结果集越小,作用越大,比如对于性别列作用不大。
使用短索引。
利用最左前缀。
普通索引均会保存主键键值,故主键尽可能选择较短的数据类型。
HASH索引于BTREE索引:

  1. 只能使用=和<=>进行匹配。
  2. 无法进行排序优化。

第11章 视图

不可更新的视图:

  1. 包含聚合函数(SUM, MIN, MAX, COUNT等),DISTINCT, GROUP BY, HAVING, UNION, UNION ALL语句。
  2. 常量视图。
  3. SELECT包含子查询。
  4. FROM一个不能更新的视图。
  5. WHERE中的子查询引用了FROM中的表。

WITH CASCADED | LOCAL CHECK OPTION: 满足该视图即可更新 | 满足所有相关视图才可更新。

SHOW TABLE STATUS FROM DB LIKE PATTERN.
SHOW CREATE VIEW VIEW_NAME
information_schema.views

第12章 存储过程和函数

创建二者需要CREATE ROUTINE权限,修改需要ALTER ROUTINE权限,执行需要EXECUTE权限。
DELIMITER $$可修改语句结束标示。
查看方式与VIEW的查看方式类似。

  1. 事件调度器
  2. 流程控制(IF, CASE, REPEAT等)
  3. 光标
  4. 条件和处理
  5. 变量

第13章 触发器

第14章 事务控制与锁定语句

(UN)LOCK TABLE.
事务控制中SAVEPOINT的使用。
分布式事务命令XA,prepare状态下出现异常,则 binlog不会存在记录,会使数据不一致。数据完整性要求不高时可使用。

第15章 SQL中的安全问题

防止SQL注入:

  1. 对用户输入进行转义,一般的ORM框架可实现。
  2. 使用数据库提供的转换函数,quote等。
  3. 代码敏感字符拦截。

SQL MODE

不同严格程度SQL语法和数据校验。

SQL_MODE的常见功能:

  1. ANSI和TRADITIONAL: 允许和不允许插入非法时间值,MOD(X, 0)等。
  2. NO_BACKSLASH_ESCAPES: 视 为普通字符。
  3. PIPES_AS_CONCAT: 视 || 为字符串连接符使其不报错。

常用SQL_MODE:

  1. ANSI: 宽松模式。
  2. STRICT_TRANS_TABLE: 严格模式。
  3. TRADITIONAL: 严格中的严格模式。

第16章 SQL分区

SHOW VARIABLES LIKE ‘partition’ 查看是否支持分区。
创建表时使用partition关键词设置分区。
若有主键或唯一键,则分区关键词必须是其中之一。
分区类型:

  1. RANGE: 按照取值范围进行分区。NULL值做最小值处理。
  2. LIST: 利用数组值包含关系进行分区。NULL值如果被允许则必须在候选项中。
  3. COLUMNS: 多列般的RANGE AND LIST(RANGE COLUMNS AND LIST COLUMNS关键词)。
  4. HASH: 常规HASH, 列关键词 % 分区数量确定分区号。当需要增加分区时,已有数据迁移代价太大。LINER HASH, 使用更加复杂的算法进行分区使上述得到优化,但使数据分布不均匀。二者均只支持整数分区(非整数列需要使用函数转换)。NULL被视为0值。
  5. KEY: 支持除BLOB和TEXT外的所有类型列作为分区键。可不指定分区键,默认使用主键或唯一索引列(非空才不会报错)。支持多分区键,同样也可以使用LINIRE KEY。NULL被视为0值。

## 子分区(复合分区)
可对每个分区(RANGE AND LIST)进行再分区(HASH AND SET)。
## 分区管理

SQL优化

  1. SHOW (global | session) STATUS LIKE (Com_% | Innodb_rows% | Connections | Uptime | Slow_queries) 查看命令执行DASHBOARD。
  2. —log-slow-queries=file_name 记录慢查询。
  3. Using explain or desc to analytic sql.
  • select_type: SIMPLE 简单表,PRIMARY: 外层查询,UNION: UNION中的第二个或后面的查询,SUBQUERY: 子查询的第一个SELECT。
  • type:访问类型,性能由高到低为:

    • NULL: 无需访问即可得出答案。
    • const/system: 仅一个匹配行,如单表查询中使用主键和唯一键进行查询。
    • eq_ref: 多表连接中使用主键或唯一键进行关键。
    • ref: 使用非唯一索引或唯一索引的前缀匹配。
    • ref_or_null: 查询条件中包含对NULL的查询。.
    • index_merge: 索引合并优化。
    • unique_subquery: in后面是一个查询主键字段的子查询。
    • index_subquery: in后面是一个针对非唯一索引字段的子查询。
    • range: 索引范围扫描,如对于索引列的大于小于between操作。
    • index: 索引全扫描,遍历整个索引查询匹配到的行。如查询一个被索引的列。
    • ALL: 全表扫描。
  1. explain extended and then show warnings to show the actual SQL that has been executed after SQL optimization. EXPLAIN PARTITIONS 可用于查看语句将操作的分区。
  2. SHOW PROFILE(S):
  • SELECT @@have_profiling 查看是否支持profile。
  • SELECT @@profiling 查看是否开启此功能(默认关闭)。
  • SET profiling=1 开启此功能。
  • SHOW PROFILES查看历史SQL及耗时。
  • SHOW PROFILE (ALL | CPU | block io | source/源文件 etc.) ROR QUERY x 查看某个SQL的各阶段耗时。

    • sending data 状态表示开始访问数据行并把结果返回,往往需要大量磁盘IO,故往往是最耗时的。
  • INFORMATION_SCHEMA.PROFILING 存有profiling信息。
  1. trace: 查看优化依据。
  • SET OPTIMIZER_TTACE=“enabled=on”, END_MARKER_IN_JSON=on;
  • SET OPTIMZER_TRACE_MAX_MEM_SIZE=10000000
  • INFORMATION_SCHEMA.OPTIMIZER_TRACE

索引

  • B-Tree: 广泛使用。B代表balanced,其非二叉树。
  • HASH: 仅Memory引擎支持。无法用在范围查找。
  • R-Tree: 空间索引,MyISAM的一个特殊索引类型,主要用于地理空间数据类型。
  • Full-text: 全文索引。

前缀索引在Order By和Group By时无法使用。且无法实现覆蓋索引扫描。

Using index: 覆蓋索引扫描。所选择的字段即为索引,无需额外访问。
Using where: 根据索引回表访问数据。需要额外数据访问或筛选。
Using index condition: 使用ICP(Index Condition Pushdown)的特征,某些情况下无需访问数据后筛选。

无法使用索引:

  1. LIKE “%xx”。
  2. 存在数据类型隐式转换。如last_name=1
  3. 复合索引不包含最左边列,即不满足最左原则。
  4. MySQL估计使用索引比全表扫描更慢。比如last_name LIKE “T%”。使用trace可查看依据。
  5. OR左侧有索引,右侧无索引,涉及的索引则均不会被使用。(反正都要全表扫描,没必要多一次索引扫描增加IO访问)

SHOW STATUS LIKE “Handler_read%”: key表一个行被索引值读的次数,越高越好。rnd_next表数据文件中读下一行的请求数,越低越好。

定期检查表: (analyze | check) table payment.
定期优化表: 若删除了表的一大部分(另可使用alter table payment engine=innodb回收不用的空间),对含有可变长度行(VARCHAR, TEXT, BLOB等)的表进行了很多更改,应使用optimize table payment

常用优化

  • 大批量插曲数据

    • 文本数据采用load data infile 方式导入。
    • SET (UNIQUE_CHECKS | AUTOCOMMIT) =0。
  • 优化ORDER BY
    Using index: 有序索引顺序扫描直接返回有序结果。select if from a order by id;
    Using filesort: 对返回的数据进行排序。不是通过索引直接返回排序结果的排序都叫filrsort,即时使用索引列排序依然会出现Using filesort. select * from a order by id; more puzzles…

    where and order by 使用相同的索引并且order by的顺序与索引顺序相同且排序均为升序或降序。
    sort_buffer_size: 线程独占。
    max_length_for_sort_data: 若读取的字段总大小小于此值,则一次读取满足条件的所有行,然后在排序区sort buffer中排序后直接输出结果,内存消耗较大但效率高。否则仅取出排序字段和指针进行上述操作,若sort buffer不够,则在临时表中存储排序结果。排序后根据指针回表读取记录。

  • GROUP BY

    • 在其后主动添加ORDER BY NULL可取消其默认的排序行为,增加效率。
  • 嵌套查询

    • 没有连接查询快
  • OR
  • 分页

    • 根据索引查出满足条件的所有行的某个字段后连表。
  • 使用SQL提示

    • SELECT SQL_BUFFER_RESULTS FROM table_name: 生成临时结果集,可释放锁慢慢将数据回传给客户端。
    • use index: 指定希望MySQL去参考的索引。
    • ignore index: 忽略某些索引。
    • force index:

ORDER BY RAND()实现随机排序。
WITH ROLLUP与ORDER BY互斥。
BIT_AND(),BIT_OR()聚合函数进行位运算。

第19章 优化数据库对象

SELECT * FROM table PROCEDURE ANALYSE(): 让MySQL根据已存在的数据特征给出优化建议。
数据拆分: 垂直拆分,不常用列单独存储。水平拆分,不同时期数据于不同表或分区存储。
逆规范化: 增加冗余列。组新表。
维护数据完整性: 定时任务,触发器。
使用中间表提高统计效率。

第20章 锁

MyISAM, MEMORY: 表级锁。开销小,加锁快,粒度大。适合以查询为主,只有少量按索引条件更新数据的应用,如WEB。
InnoDB: 行级锁,也支持表级锁。开销大,加锁慢,粒度小。并发度高。适合有大量按索引条件并发进行少量数据更新,同时又有并发查询需求的应用如在在线事务系统。
DBD: 页级锁,也支持表级。介于上二者之间。

表锁(MyISAM)

SHOW STATUS LIKE ‘table%’查看表锁争用情况。
读锁可存在多个,有且只有一个写锁,写锁与读锁不共存。
LOCK TABLE(S) table (as t) WRITE给表加锁。表使用别名时需要为不同的别名都加锁。
UNLOCK TABLES释放锁。

在自动加锁的情况下,MyISAM总是直接获取SQL所需要的所有锁,所以MyISAM不会出现死锁。

系统变量 concurrent_insert:

  1. 0: 不允许并发插曲
  2. 1: 如果MyISAM表中间没有空洞,即没有删除行,允许读锁存在时另一进程在表尾插入记录。MySQL的默认值,OPTIMIZE TABLE可整理空间碎片。
  3. 2: 不管有无空洞均允许表尾插入。

锁调度机制: 等待队列中有写锁请求页给予最高优先级而与入列顺序无关。这也是MyISAM不适合有大量更新和查询操作的应用的原因。各类操作优先级可设定。maz_write_lock_count可设定当读请求超过一定值后降低写操作的优先级。

InnoDB锁问题

于MyISAM: 有了事务和行级锁。

并发事务带来的问题:

  1. 更新丢失
  2. 赃读
  3. 不可重复读: 二次读时数据被更改或删除。
  4. 幻读: 二次读时有新的满足查询条件的结果。

SELECT @@tx_isolation
事务隔离级别:

  1. 未提交读: 读未提交不读已损坏。
  2. 已提交读: 读已提交。
  3. 可重复读:
  4. 可序列化

SHOW STATUS LIKE “innodb_row_lock%”查看行锁争用情况。
SELECT * FROM information_schema.innodb_locks查看锁等待情况。
通过InnoDB Monitors观察锁冲突情况。(show engine innodb status)

InnoDB加锁方法

SELECT … LOCK IN SHARE MODE.
SELECT … FOR UPDATE.

行锁情形:

  1. record lock: 对索引项加锁
  2. gap lock: 对记录之间的间隙加锁,主要作用于范围筛选。可解决幻读问题。对于不存在的记录,也会使用此锁,故不存在的记录也会存在冲突。
  3. next-key lock: 对记录及其前后间隙加锁。

死锁:

  • 如果一个事务先对某行加共享锁(SSLECT)后又需要更新该行数据(UPDATE等),高并发环境下很可能会造成死锁。
  • InnoDB的行锁时通过给索引上的索引项加锁实现的,如果没有索引,将通过隐藏的聚簇索引来对记录加锁。如果不通过索引条件检索数据,InnoDB将对所有记录加锁(表锁)。
  • InnoDB是针对索引项加锁,故如果使用同一索引键操作不同行的记录,也会出现行冲突。
  • 通过不同索引键获取同一记录的锁也会冲突。
  • 是否使用索引是MySQL通过计算不同执行计划的代价决定的,所以分析锁冲突时,有时需要检查SQL的执行计划。

BINLOG, 执行Log.

set innodb_locks_unsafe_for_binlog=‘on’: 在UPDATE INTO target … SELECT …source和CREATE TABLE target … SELECT … source时不给source加锁。这会造成并发更新source相关字段时(不会被阻塞),binlog中日志顺序出现错误,数据恢复和复制会出现主从不一致的情况。这些SQL称为不确定的SQL(unsafe SQL)。默认为off,但对于复杂查询可能会有严重的性能问题,故可以使用导出文件再加载文件的方式。

复制模式

SET SESSION ISOLATION LEVEL SERIALIZABLE: 动态改变隔离级别。

点赞