深入浅出MySQL笔记02--开发篇
深入浅出MySQL 开发篇
1. 表类型(存储引擎)的选择
存储引擎概述
插件式存储引擎是MySQL数据库重要特性之一,可根据应用的需要选择如何存储和索引数据、是否使用事务等。MySQL默认支持多种存储引擎,以适应不同领域的db应用需求。
MySQL 5.0 支持的存储引擎:MyISAM, InnoDB, DBD, MEMORY, MERGE,等。其中InnoDB, DBD提供事务安全表,其他都是非事务安全表。
MySQL 5.5 之前的默认存储引擎是MyISAM,5.5 之后改为了InnoDB。
各种存储引擎的特性
1. MyISAM
不支持事务、不支持外键,优势是访问速度快,对事务完整性没有要求或以SELECT INSERT为主的应用可以使用这个引擎创建表。
每个MyISAM在磁盘上存储成3个文件,其文件名为表名,扩展名分别是
.frm (存储表定义)
.MYD (MYData, 存储数据)
.MYI (MYIndex, 存储索引)
MyISAM表还支持3种不同的存储格式:
静态(固定长度)表
动态表
压缩表
静态表是默认的存储格式,表中字段都是非变长字段,从而每个记录长度固定,优点是存储迅速,容易缓存,出故障易恢复;缺点是占用空间比动态表多。静态表数据在存储时会按列宽定义补足空格,应用访问时空格在返回给应用之前被去掉。 动态表中包含变长字段,记录长度不固定,占用空间少,但频繁地更新和删除记录会产生碎片,需定期执行OPTIMIZE TABLE 或 myisamchk-r来改善命令。出故障不易维护。 压缩表由myisampack工具创建, 占据非常小的磁盘空间。每个记录被单独压缩,只有非常小的访问开支。
2. InnoDB
提供了具有提交、回滚和崩溃恢复能力的事务安全。对比MyISAM,InnoDB写的处理效率差一些,并会占用更多的磁盘空间以保留数据和索引。
InnoDB的表的特点:
自动增长列
定义方式:col_def auto_increment
eg: create table tname (i smallint not null auto_increment)
设置自动增长列的初始值:ALTER TABLE *** AUTO_INCREMENT = n (默认从1开始)
查询当前线程最后插入记录使用的值:select LAST_INSERT_ID();
对于InnoDB,自动增长列必须是索引,也必须是组合索引的第一列。
外键约束
MySQL支持外键的存储引擎只有InnoDB。在创建外键时,要求父表必须有对应的索引,子表在创建外键时也会自动创建对应的索引。
创建索引时,可以指定在删除、更新父表时,对子表进行的相应操作,包括:
- RESTRICT:限制在子表有关联记录的情况下父表不能更新
- CASCADE: 父表在更新和删除时,更新或删除子表对应记录
- SET NULL: 父表在更新和删除时,子表对应记录的对应字段被SET NULL
- NO ACTION: 同RESTRICT
- 当某个表被其他表创建了外键参照,该表的对应索引或主键禁止被删除。
- 查看外键信息:show create table 和 show table status
存储方式
- 使用共享表空间存储,创建的表的表结构存在.frm 文件中,数据和索引存在innodb_data_home_dir 和innodb_data_file_path定义的表空间中,可以是多个文件。
- 使用多表空间存储,创建的表的表结构存在.frm 文件中,但每个表的数据和索引单独保存在.ibd中。如果是分区表,则每个分区对应单独的.ibd文件,文件名是“表名+分区名”。
多表空间的数据文件无大小限制,不需设置初始大小和文件的最大限制、扩展大小等参数。
3. MEMORY
使用存在于内存中的内容创建表。每个MEMORY表只对应一个磁盘文件,格式是.frm。MEMORY表访问非常快,因为数据放在内存中,且默认用HASH索引(创建索引时可以指定为BTREE),但一旦服务关闭,表中数据就会丢掉。
服务器需要足够内存维持所有同一时间使用的MEMORY表,当不再需要MEMORY表时,需要执行DELETE 或 TRUNCATE TABLE或DROP TABLE释放其占用的内存。
MEMORY主要用于内容变化不频繁的代码表,或作为统计操作的中间结果表,便于高效对中间结果进行分析。
4. MERGE
是一组MyISAM表的组合,这些MyISAM表必须结构完全相同,MERGE表本身没有数据,对MERGE表的删改查操作实际是对内部的MyISAM表进行的。对MERGE表的插入操作通过INSERT_METHOD子句定义插入的表。
对MERGE表的DROP操作只是删除MERGE表的定义,对内部表没有任何影响。
MERGE表 vs 分区表:MERGE表并不能智能地将记录写到对应的表中,分区表可以。通常使用分区表透明地对多个表进行查询和更新操作。
2. 合适的数据类型
- 对于字符类型,要根据存储引擎进行相应的选择
- 对精度要求较高的应用中,建议用定点数存储数值,以保证结果的正确性
- 对含有TEXT和BLOB字段的表,如果经常增删记录,要定期执行OPTIMIZE TABLE对表进行碎片整理
- 日期类型如果要让不同时区的用户使用,最好使用TIMESTAMP,因为日期类型中只有它能和实际时区对应。
3. 字符集
- 如果应用需要处理各种文字,或将发布到不同语言的国家地区,应该选择Unicode(对MySQL来说就是UTF-8)字符集。
- 如果数据库只需要支持一般中文,数据量大且要求性能,应该选择双字节定长编码的中文字符集,eg:GBK。GBK中每个汉字只占2字节,而UTF-8汉字编码是3字节。
- 如果应用主要处理英文字符,UTF-8更好,因为GBK等的西文编码也是2字节。
- 如果数据库需要大量的比较、排序等字符运算,那么选择定长字符集处理速度更快。
4. 索引
所有MySQL列类型都可以被索引。MyISAM和InnoDB的表默认创建的是BTREE索引;MEMORY默认使用HASH索引。
设计索引的原则:
- 最适合索引的列是出现在WHERE子句中的列,或连接子句中指定的列
- 使用唯一索引:索引的列的基数(集合里的概念,不同的值的个数)越大,索引效果越好。eg,在出生日期列建索引比在性别(只有两个值)列效果好。
- 使用短索引:如果对字符串列进行索引,应尽可能指定一个前缀长度。短索引节省索引空间,也可能使查询更快;此外,对于较短的键值,索引高速缓存中的块能容纳更多键值,增加了找到行而不用读取索引中较多块的可能性。
- 利用最左前缀:
- 不要过度索引:额外的索引会占用额外磁盘空间,降低写操作的性能。修改表内容时,索引也需要更新甚至重构。
- 对于InnoDB的表
- 记录默认按照一定的顺序保存
- 有明确定义的主键:按主键顺序
- 无明确定义的主键但有唯一索引:按唯一索引顺序
- 都无:自动生成一个内部列,按该列顺序
- 按照主键或内部列进行访问是最快的,所以InnoDB表尽可能指定主键
- 当表中多个列都是唯一时,选最常作为访问条件的列作为主键
- InnoDB表的普通索引也会保存主键的键值,所以主键要尽可能短
- 记录默认按照一定的顺序保存
BTREE 索引和HASH索引
- HASH索引特点:
- 只用于使用=或<=>操作符的等式比较
- 优化器不能使用HASH索引加速ORDER BY
- 只能用整个关键字来搜索一行
- 对于BTREE索引,使用> , < , >= , <= , BETWEEN , != 或者 <> , 或者 LIKE (模式不以通配符开始)时,都可以使用相关列上的索引
5. 视图
视图(view)是一种虚拟存在的表,并不在数据库中实际存在,使用透明。
视图相对于普通的表优势:
- 简单:用户无需关心后面对应的表结构、关联条件、筛选条件,对用户来说是过滤好的复合条件的结果集。
- 安全:用户只能访问被允许查询的结果集
- 数据独立:一旦视图结构确定,可以屏蔽表结构变化对用户的影响
视图操作
创建或修改视图
创建视图需要有CREATE VIEW 权限,并对查询涉及的列有SELECT 权限。若使用CREATE OR REPLACE 或ALTER修改视图,还需要视图的DROP权限。
创建视图语法:
CREATE [OR REPLACE]
VIEW vname [(col_list)]
AS select_statement
修改视图语法:
ALTER
VIEW vname [(col_list)]
AS select_statement
删除视图
DROP VIEW [IF EXISES] vname
查看视图
MySQL 5.1开始 SHOW TABLES [STATUS]同时会显示视图信息。
6. 存储过程和函数
存储过程和函数是事先经过编译并存储在数据库中的一段SQL语句的集合,调用存储过程和函数可以简化工作,减少数据在数据库和应用服务器之间的传输,利于提高数据处理效率。
存储过程和函数的区别在于函数必须有返回值,而存储过程没有,存储过程的参数可以使用IN, OUT, INOUT类型,函数的参数只能是IN。
相关权限
- 创建:需要CREATE ROUTINE 权限
- 修改或删除:需要ALTER ROUTINE 权限
- 执行:需要EXECUTE 权限
创建or修改存储过程和函数
CREATE PROCEDURE sp_name ([proc_param]) [characteristic] routine_body
CREATE FUNCTION sp_name ([func_param]) RETURNS type [characteristic] routine_body
调用
CALL sp_name ([proc_param])
存储过程的好处
处理逻辑封装在数据库端,调用者不需要了解中间的处理逻辑,一旦处理逻辑发生改变,只需要修改存储过程,而对调用者的程序完全没有影响。
7. 触发器
触发器是与表有关的数据库对象,在满足定义条件时触发,并执行触发器中定义的语句集合。可以协助应用在数据库端确保数据的完整性。
创建触发器
CREATE TRIGGER trigger_name trigger_time trigger_event
ON tb1_name FOR EACH ROW trigger_stmt
触发器只能创建在永久表上, 不能对临时表创建触发器。
- trigger_time :触发时间,可以是
- BEFORE , 在检查约束前触发
- AFTER , 在检查约束后触发
- trigger_event:触发事件,可以是增删改
对同一个表相同触发时间的相同触发事件,只能定义一个触发器。
可以使用别名OLD和NEW来引用触发器中发生变化的记录内容。
触发顺序
- 对于有重复记录、需要进行UPDATE 操作的INSERT,触发器触发的顺序是BEFORE INSERT -> BEFORE UPDATE -> AFTER UPDATE;
- 对于没有重复记录的INSERT,就是简单执行INSERT, 触发顺序是BEFORE INSERT -> AFTER INSERT
- 对实际执行UPDATE 操作的记录,仍会执行BEFORE INSERT触发器的内容
8. 事务控制和锁定语句
默认情况下,表锁和行锁都是自动获得的,不需要额外命令。但是在有的情况下,用户需要明确进行锁表或进行事务的控制,以确保事务的完整性。
LOCK TABLE 与 UNLOCK TABLE
- LOCK TABLES 锁定用于当前线程的表,如果表被其他线程锁定,则当前线程会等待,直到可以获取所有锁定为止
- UNLOCK TABLES 释放当前线程获得的任何锁定
事务控制
默认情况下,MySQL是自动提交的。
- START TRANSACTION 或BEGIN 开始一项新的事务
- COMMIT 和 ROLLBACK 用来提交或回滚事务
- CHAIN 和 RELEASE 子句分别定义事务提交或回滚之后的操作,
- CHAIN 会立即启动一个新事务,并和刚才的事务有相同的隔离级别;
- RELEASE 会断开和客户端的连接
- SET AUTOCOMMIT (=0) 修改当前连接的提交方式,设为0时之后的所有事务都需要明确的命令进行提交或回滚。
如果在锁表期间,用start transaction开始一个新事务,会隐含执行 unlock tables
9. SQL中的安全问题
SQL注入
10. MySQL 分区
分区是指根据一定的规则,数据库把一个表分解成多个更小的、更容易管理的部分。对于访问数据库的应用来说,逻辑上只有一个表或一个索引,实际上这个表可能有数10个物理分区对象组成,每个分区都是一个独立对象,可以独自处理,可以作为表的一部分处理。分区对于应用来说是完全透明的,不影响应用的业务逻辑。
分区的优点
- 和单个磁盘或文件系统相比,可以存储更多数据
- 优化查询。在Where子句中包含分区条件时,可以只扫描必要的一个或多个分区来提高查询效率;同时在涉及SUM和COUNT这类聚合函数的查询时,可以容易地在每个分区上并行处理,最终只需要汇总所有分区得到的结果
- 对于已经过期或不需要保存的数据,可以删除与这些数据有关的分区来快速删除数据
- 跨多个磁盘分散数据查询,以获得更大的查询吞吐量
分区概述
分区有利于管理非常大的表,采用了分治逻辑,分区引入了分区键的概念。分区键用于根据某个区间值、特定值列表、HASH函数值执行数据的聚集,让数据根据规则分布在不同的分区中。
分区类型
- RANGE分区:基于一个给定连续区间范围,把数据分配到不同分区
- LIST分区:基于枚举出的值列表分区
- HASH分区:基于给定的分区个数,分配数据
- KEY分区:类似HASH分区
注意:无论哪种分区类型, 都不能用主键/唯一键字段之外的其他字段分区
Range分区
利用取值范围将数据分成分区,区间要连续且不能互相重叠,使用VALUES LESS THAN进行分区的定义。
注意:每个分区都按顺序进行定义,从最低到最高。
可以使用VALUES LESS THAN MAXVALUE子句提供给所有大于明确指定的最高值的值。
特别适用于:
- 需要删除过期数据,eg:ALTER TABLE tname DROP PARTITION p0
- 经常运行包含分区键的查询
List 分区
建立离散的值列表告诉数据库特定的值属于哪个分区。
使用PARTITION BY LIST + VALUES IN (X,X)
LIST 分区不需要声明任何特定的顺序。
若试图插入的列值不包含在分区值列表中时,INSERT会失败并报错。
Columns分区
可以细分为RANGE Columns和LIST Columns分区,都支持整数、日期时间、字符串三大数据类型。
Columns分区还支持多列分区。
Hash分区
主要用来分散热点读,确保数据在预先确定个数的分区中尽可能平均分布。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!