Innodb存储引擎,每次读取16k
最上层是一些客户端和连接服务,包含本地sock通信和大多数基于客户端/服务端工具实现的类似于TCP/IP的通信。主要完成连接处理,授权认证以及相关的安全方案。该层引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它具有的操作权限。
大部分核心服务功能在该层实现。如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有的跨存储引擎功能也在这一层实现,如过程,函数等。在该层,服务器会解析查询并创建相应的内部解析树,并完成相应的优化,如确定表的查询顺序,是否利用索引等,最后生成相应的执行操作。如果是select语句,服务器还会查询内部的缓存,如果缓存空间足够大,可以在大量的读操作中提高系统性能。
存储引擎层, 存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。数据库的索引(Index)是在存储引擎层实现的。在MySQL5.5版本之后,默认的存储为Innodb。
数据存储层, 主要是将数据(如: redolog、undolog、数据、索引、二进制日志、错误日志、查询、日志、慢查询日志等)存储在文件系统之上,并完成与存储引擎的交互。
和其他数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎上,插件式的存储引擎架构,将查询处理和其他的系统任务以及数据的存储提取分离。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。
MySQL数据库的核心,我们需要在合适的场景选择合适的存储引擎。
存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式 。存储引擎是基于表的,而不是基于库的,所以存储引擎也可被称为表类型。我们可以在创建表的时候,来指定选择的存储引擎,如果没有指定将自动选择默认的存储引擎。在MySQL5.5版本之后,默认的存储为Innodb。
(1)建表指定引擎
create table 表名()engine=innodb
(2)查询当前数据库支持引擎
show engines;
(3)查询建表语句
show create table 表名
InnoDB是一种兼顾高可靠性能的通用存储引擎,在MySQL5.5之后是默认的MySQL存储引擎。
特点
文件
xxx.ibd:innoDB引擎的每张表都会对应这样一个表空间文件,存储该表结构(frm-早期的格式、新版的)、数据和索引。该文件是基于二进制存储的,不能直接打开,可以用MySQL提供的指令 ibd2sdi 名.idb 通过该指令就可以从ibd文件中提取sdi信息,而sdi数据字典信息中就包含该表结构。
逻辑存储结构
MyISAM是早期MySQL早期的默认存储引擎
特点
文件
xxx.sdi:存储表结构信息
xxx.MYD:存储数据
xxx.MYI:存储索引
Memory引擎的表数据存储在内存中,由于受到硬件问题或断电问题,智只能将这些表作为临时表或缓存使用。
特点
文件
xxx.sdi:存储表结构信息
特点 | InnoDB | MyISAM | Memory |
---|---|---|---|
存储限制 | 64TB | 有 | 有 |
事务安全,外键 | 支持 | - | - |
锁机制 | 行锁 | 表锁 | 表锁 |
B+Tree索引 | 支持 | 支持 | 支持 |
Hash索引 | - | - | 支持 |
全文索引 | 支持(5.6版本之后) | 支持 | - |
空间使用 | 高 | 低 | N/A |
内存使用 | 高 | 低 | 中等 |
批量插入速度 | 低 | 高 | 高 |
在选择存储引擎时,应该根据应用的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。
索引(index)是存储表中的一个特定列值数据结构(最常见的B-Tree) 。索引是在表的列上创建。索引是一种数据结构。
索引是帮助MySQL高效获取的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。
例如:我们要执行查询语句
(1)无索引情况,就需要从第一行扫描到最后一行。称之为全表扫描,性能很低。
(2)有索引情况,假如对这张表建立了索引结构为二叉树,对age建立一个二叉树索引结构。如下图:
此时只需要扫描几次,就可以找到需要的数据了,极大的提高了查询的效率。
特点
优势 | 劣势 |
---|---|
提高数据检索的效率,降低数据库的IO成本 | 索引列也是要占用空间的 |
通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗 | 提高查询效率的同时降低更新表(insert,update,delete)的速度 |
MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的结构。
索引结构 | 描述 |
---|---|
B+Tree索引 | 最常见的索引类型,大部分引擎都支持B+树索引 |
Hash索引 | 底层是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询。(如memory引擎) |
R-tree(空间索引) | 空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间的数据类型,通常使用较少。 |
Full-text(全文索引) | 一种通过建立倒排序索引,快速匹配文档的方式,类似于Lucene,Solr,ES |
引擎支持
索引 | InnoDB | MyISAM | Memory |
---|---|---|---|
B+Tree索引 | 支持 | 支持 | 支持 |
Hash索引 | - | - | 支持 |
R-tree索引 | - | 支持 | - |
Full-text | 5.6版本后支持 | 支持 | - |
理想结构如下:
但是如果是顺序插入就会生成一个单向列表,结构如下:
二叉树作为索引结构缺点
红黑树
选择红黑树即使在顺序插入数据,最终形成的数据结构也是一颗平衡的二叉树。结构如下:
红黑树:图解插入示例
即使如此,红黑树也是二叉树,也会存在同样的缺点:
B-Tree,B树是一种多叉路平衡查找树,B树每个节点可以有多个分支,及多叉。一棵树的最大度数(max-degree)为5(5阶)的b-tree为例,这个B树每个节点最多存储4个key,5个指针。
B-Tree可视化网站
MySQL索引数据结构对B+Tree进行了优化。在B+Tree基础上,增加了一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问性能。
B+Tree可视化网站 B+Tree介绍
结构如下:
B+Tree与B-Tree的区别
MySQL中除了支持B+Tree索引,还支持一种索引类型–Hash索引,如Mermory存储引擎就支持Hash索引。
哈希表(hash)表
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
数组内存是连续的,可以直接通过下标访问,随机访问,但是插入修改困难。而链式链表是用一组任意的存储单元来存储数据,在插入和删除时,可以通过修改指针完成。综合起来就是一个链表的数组。如哈希表的一种实现方法–拉链法。
(1)结构
哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。
(2)特点
(3)存储引擎支持
在MySQL中,只有memory存储引擎支持Hash索引。而InnoDB中有自适应hash功能,而hash索引是InnoDB存储引擎根据B+Tree索引在指定条件下构建的。
为什么InnoDB使用B+Tree索引结构?
分类 | 含义 | 特点 | 关键字 |
---|---|---|---|
主键索引 | 主键创建的索引 | 默认创建,只能有一个 | PRIMARY |
唯一索引 | 避免同一个表中数据列的值重复 | 可以有多个 | UNIQUE |
常规索引 | 快速定位特定数据 | 可以有多个 | |
全文索引 | 全文索引查找的是文本中的关键词,而不是比较索引中的值 | 可以有多个 | FULLTEXT |
存储形式分类
分类 | 含义 | 特点 |
---|---|---|
聚集索引(ClusteredIndex) | 将数据存储于索引放到一块,索引结构的叶子节点保存了行数据 | 必须有而且只有一个 |
二级索引(SeconddaryIndex) | 将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 | 可以存在多个 |
聚集索引选取规则:
①. 由于是根据name字段进行查询,所以先根据name='Arm’到name字段的二级索引中进行匹配查找。但是在二级索引中只能查找到 Arm 对应的主键值 10。②. 由于查询返回的数据是*,所以此时,还需要根据主键值10,到聚集索引中查找10对应的记录,最终找到10对应的行row。 ③. 最终拿到这一行的数据,直接返回即可。
回表查询:先到二级索引中查找数据,找到主键值,然后再到索引中根据主键值,获取数据的方法。
创建索引
CREATE [ UNIQUE | FULLTEXT ] INDEX index_name ON table_name (index_col_name,... ) ;
查看索引
SHOW INDEX FROM table_name
删除索引
DROP INDEX index_name on table_name
建表
CREATE TABLE tb_user( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键', uname VARCHAR(50) NOT NULL COMMENT '用户名', phone VARCHAR(11) NOT NULL COMMENT '手机号', email VARCHAR(100) COMMENT '邮箱', profession VARCHAR(11) COMMENT '专业', age TINYINT UNSIGNED COMMENT '年龄', gender CHAR(1) COMMENT '性别 , 1: 男, 2: 女', STATUS CHAR(1) COMMENT '状态', createtime DATETIME COMMENT '创建时间' ) COMMENT '系统用户表';
需求如下
(1) name字段为姓名字段,该字段的值可能会重复,为该字段创建索引。
create index index_user_uname on tb_user(uanme);
(2) phone手机号字段的值,是非空,且唯一的,为该字段创建唯一索引。
create unique index index_user_phone on tb_user(phone);
(3)为profession、age、status创建联合索引(它有顺序之分)。
create index index_user_pro_age_sta on tb_user(profession,age,status);
(4)为email建立合适的索引来提升查询效率。
create index index_email on tb_user(email);
MySQL客户端连接成功后,通过show [session/global] status 命令可以提供服务器状态信息。通过指令可以查看当前数据库insert,update,delete的访问频次;
-- session 是查看当前会话 ; -- global 是查询全局数据 ;
show global status like 'Com_______';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_binlog | 0 |
| Com_commit | 20 |
| Com_delete | 1 |
| Com_insert | 6399 |
| Com_repair | 0 |
| Com_revoke | 0 |
| Com_select | 179 |
| Com_signal | 0 |
| Com_update | 6 |
| Com_xa_end | 0 |
+---------------+-------+
慢查询日志记录了所有执行时间超过参数(long_query_time,默认10秒)的所有sql语句的日志。
mysql> show variables like 'slow_query_log';
+----------------+-------+
| Variable_name | Value |
+----------------+-------+
| slow_query_log | ON |
+----------------+-------+
如果要开启需要在MySQL的配置文件中配置以下信息
# 开启MySQL慢日志查询开关 slow_query_log=1
# 设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志 long_query_time=2
MySQL5.7–配置慢查询(windows10)
通过慢查询日志,就可以定位出执行效率比较低的SQL,从而有针对性的进行优化。
查看profile能够帮助我们了解时间都耗费到哪里去了。通过 select @@profiling参数,能够看到当前MySQL是否支持profile操作
select @@having_profile;-- 查看当前是否支持查看profile
执行一系列的业务SQL操作执行以下指令的执行耗时;
-- 查看每一条SQL的耗时基本情况
show profiles;
-- 查看指定query_id的SQL语句各个阶段的耗时情况
show profile query id;
-- 查看指定query_id的申SQL语句的CPU使用情况
show profile cpu for query id;
explain 或者 desc命名获取如何执行select语句的信息,包括在select语句执行过程中如何连接表的顺序。
-- 直接在select语句之前加上关键字
explain / desc EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件 ;
验证索引效率:直接查看语句执行时间
如果索引了多列(联合索引),要遵守最左前缀法则。就是从索引的最左列开始并不跳过索引中的列。如果跳跃某一列,索引可能会部分失效(后边的字段索引失效)
(貌似与where后的语句顺序无关,语法优化器会帮我们优化为按照索引的顺序)
看key_len的大小;来判断走的索引
如果MySQL评估使用索引比全表扫描更慢,就不使用索引。不是聚集索引的话就会,先走索引找到主键值回表扫描。
就是因为MySQL在查询时,会评估使用索引的效率与走全表扫描的效率,如果走全表扫描更快,则放弃索引,走全表扫描。 因为索引是用来索引少量数据的,如果通过索引查询返回大批量的数据,则还不如走全表扫描来的快,此时索引就会失效。
is null is not null
一模一样的SQL语句,先后执行了两次,结果查询计划是不一样的,为什么会出现这种现象,这是和数据库的数据分布有关系。查询时MySQL会评估,走索引快,还是全表扫描快,如果全表扫描更快,则放弃索引走全表扫描。 因此,is null 、is not null是否走索引,得具体情况具体分析,并不是固定的。
(1)use index(index_name):建议MYSQL走哪一个索引完成此次查询(仅仅是建议,mysql内部会再次进行评估)
explain select * from tb_user use index(idx_user_pro) where profession = '软件工程';
(2)igore index(index_name):忽略指定的索引。
explain select * from tb_user ignore index(idx_user_pro) where profession = '软件工程';
(3)force index(index_name):强制使用索引
explain select * from tb_user force index(idx_user_pro) where profession = '软件工程';
尽量使用户覆盖索引,减少select * .**覆盖索引就是指查询使用了索引并且返回需要的列,该列在该索引中已经能够全部能够找到。**就不需要回表扫描了。
Extra | 含义 |
---|---|
Using where;Using Index | 查找使用了索引,但是需要的数据在索引列中能够找到,所以不需要回表查询数据 |
Using index condition | 查找使用了索引,但是需要回表查询数据 |
当字符串(varchar,text,longtext等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率。此时可以将字符串的一部分前缀建立索引,这样可以大大节约索引空间,从而提高索引效率。
create index index_name on table(字段名(长度))
执行流程普通索引一样,就是索引字段减少了。
可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高, 唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。
单列索引:即只包含单个列。
联合索引:一个索引包含了多个列。
(1)针对数据量大,且查询比较频繁的表建立索引。
(2)针对于常作为查询条件(where),排序(order by),分组(group by)操作的字段建立索引。
(3)尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
(4)如果是字符串类型的字段,字段的长度越长,可以针对于字段的特点,建立前缀索引。
(5)尽量使用联合索引,减少单列索引,查询时,联合索引可以覆盖索引(查询使用了索引并且返回需要的列,该列在该索引中已经能够全部能够找到)节省存储空间,避免回表,提高查询效率。
(6)需要控制索引的数量,索引越多,维护索引的代价也就越大,会影响增删改的效率(每次增删改都需要更新索引)
(7)如果索引列不能存储null值,就在创建表时使用Not null约束它。优化时知道每列是否包含null值时,可以更好的确定哪个索引最有效地用于查询。
插入数据时,可以批量插入数据。或者提交一定数量的值之后,手动提交事务。按照主键顺序插入,性能要高于乱序插入。
-- 批量插入数据
Insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry'); 1
-- 手动控制事务
start transaction;
insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
insert into tb_test values(4,'Tom'),(5,'Cat'),(6,'Jerry');
insert into tb_test values(7,'Tom'),(8,'Cat'),(9,'Jerry');
commit;
-- 主键顺序插入
批量插入大批数据
如果一次性需要插入大批量数据(如几百万条记录),使用insert语句插入性能较低,可以使用MYSQL数据库提供的load指令进行插入。
use database_name;
-- 设置全局local_infile为1开启从 本地加载文件导入 数据的开关
set global local_infile = 1;
-- 执行load指令将准备好的数据加载到表结构中
load data local infile '文件路径' into table tableName fields terminated by '分隔符' lines terminated by '结束符' ;
-- 这里的文件路径为反斜杠/
在InnoDB存储引擎中,表数据都是根据主键顺序存放的,这种存储方式的表行数据都是存储在聚集索引的叶子节点上的。
索引设计原则
MySQL的排序方式 | 介绍 |
---|---|
Using filesort | 通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sortbuffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。 |
Using index | 通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要 |
Using index的性能高,而Using filesort的性能低,我们在优化排序操作时,尽量要优化为 Using index。
explain select id,age,phone from tb_user order by phone , age;
建立索引之后,再次进行排序查询,就由Extra原来的Using filesort, 变为了 Using index,性能就是比较高的了。
降序排列某列,此时Extra中出现了 Using index 但是也出现了 Backward index scan,这个代表反向扫描索引,因为在MySQL中我们创建的索引,默认索引的叶子节点是从小到大排序的,而此时我们查询排序时,是从大到小,所以,在扫描时,就是反向扫描,就会出现 Backward index scan。 在MySQL8版本中,支持降序索引,我们也可以创建降序索引。
优化原则
分组操作主要看看索引对于分组操作的影响。
优化
在数据量比较大时,
一般分页查询时,通过创建覆盖索引能够比较好的提高性能,可以通过覆盖索引加子查询形式进行优化。
explain select * from tb_sku t , (select id from tb_sku order by id limit 2000000,10) a where t.id = a.id;
如果说要大幅度提升InnoDB表的count效率,主要的优化思路:自己计数(可以借助于redis这样的数据库进行,但是如果是带条件的count又比较麻烦了)。
count用法 | 含义 |
---|---|
count(主 键) | InnoDB 引擎会遍历整张表,把每一行的 主键id 值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加(主键不可能为null) |
count(字段) | 没有not null 约束 : InnoDB 引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加。有not null 约束:InnoDB 引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,直接按行进行累加。 |
count(数字) | InnoDB 引擎遍历整张表,但不取值。服务层对于返回的每一行,放一个数字“1”进去,直接按行进行累加。 |
count(*) | InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接进行累加。 |
按照效率排序的话,count(字段) < count(主键 id) < count(*),所以尽量使用 count(*)。
我们主要需要注意一下update语句执行时的注意事项。
update course set name = 'javaEE' where id = 1 ;
当我们在执行删除的SQL语句时,会锁定id为1这一行的数据,然后事务提交之后,行锁释放.
update course set name = 'SpringBoot' where name = 'PHP' ;
当我们开启多个事务,在执行上述的SQL时,我们发现行锁升级为了表锁。 导致该update语句的性能大大降低。
InnoDB的行锁是针对索引加的锁,不是针对记录加的锁 ,并且该索引不能失效,否则会从行锁升级为表锁 。
因为name不加索引需要先去全表扫描找到对应的name值对应的记录,所以会表锁也就是锁整张表,而id是主键约束所以只是行锁.
总结
插入数据 | insert批量插入,手动控制事务,主键顺序插入 |
---|---|
主键优化 | 主键长度尽量短,顺序插入 主键自增(auto_increment) |
order by 优化 | using index:直接通过索引(覆盖索引)返回数据,性能高 Using filesort 需要将返回的结果在排序缓冲区中排序 |
group by | group by的字段为索引,多字段分组满足最左前缀法则 |
limit | 覆盖索引找id然后通过id子查询查找所有数据【】 |
count优化 | count(字段) < count(主键 id) < count(※),所以尽量使用 count(※) |
update | 尽量判断条件的时候根据主键/有索引的字段进行数据更新 |
视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图中使用的表,并且在使用视图时动态生成的。视图只保存了查询的SQL逻辑,不保存查询结果。
-- 创建视图
create or replace view as select 语句
-- 查看创建视图语句
show create view 视图名称;
-- 查看视图数
select * from 视图名称
-- 修改视图
alter view 视图名称 as select 语句
-- 删除视图
drop view if exists 视图名称
视图的更新
要是改视图更新,视图中的行与基础表之间必须存在一对一的关系,如果视图包含以下任何一项,则该视图不可更新
(1)聚合函数或窗口函数(sum(),max(),min(),count()等)
(2)DISTINCT
(3)GROUP BY
(4)HAVING
(5)UNION 或者UNION ALL
视图作用
(1)简单
视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件。
(2)安全
数据库可以授权,但不能授权到数据库特定行和特定的列上。通过视图用户只能查询和修改他们所能见到的数据
(3)数据独立
视图可帮助用户屏蔽真实表结构变化带来的影响。
存储过程是事先通过编译并存储在数据库中的一段SQL语句的集合,调用存储过程可以简化应用开发人员的工作,将减少数据在应用服务器之间的传输,对于提高数据处理是有好处的。其实就是数据库层面代码的封装与重用。
-- 存储过程基本语法
-- 删除
drop procedure if exists 存储过程名;
-- 创建
create procedure 存储过程名() --不加分号
begin
SQL语句; -- 这里的分号不要忘记了
end;
-- 调用
call 存储过程名();
-- 查看
show create procedure 存储过程名;
(1)IF
IF 条件1 THEN ... ; -- 分号要写
ELSEIF 条件2 THEN ...; -- ELSEIF必须连写
ELSE 条件3 THEN ...;
END IF;
(2)参数
参数的类型主要有三种 IN、OUT、INOUT。
类型 | 含义 |
---|---|
IN | 该参数作为输入,也就是需要调用传入的值 |
OUT | 该参数作为输出,可以作为返回值 |
INOUT | 可作为输入,输出参数 |
(3)case
create procedure p6(in month int)
begin
case
when month >= 1 and(也可用or) month <= 3 then set result := '第一季度';
when month >= 4 and month <= 6 then set result := '第二季度';
when month >= 7 and month <= 9 then set result := '第三季度';
else set result := '非法参数';
end case;
select concat('您输入的月份为: ',month, ', 所属的季度为: ',result);
end;
(4)while
满足条件进入循环
create procedure p7(in n int)
begin
declare total int default 0; -- 赋值
while n>0 do
set total:=total+n;
set n := n - 1; -- while这里要加循环结束条件可以自定义数
end while;
select total;
end;
(5)repeat
repeat是有条件的循环控制语句,当满足声明的条件的时候退出循环。
create procedure p8(in n int)
begin
repeat
set total:=total +n ;
set n:=n-1;
until n<=0
end repeat;
(6)loop
loot实现简单的循环,如果不在SQL语句逻辑中增加退出循环的条件,可以
用来实现死循环。
-- A. 定义局部变量, 记录累加之后的值;
-- B. 每循环一次, 就会对n进行-1 , 如果n减到0, 则退出循环 ----> leave xx
-- C. 如果当次累加的数据是奇数, 则直接进入下一次循环. --------> iterate xx
create procedure p10(in n int)
begin
declare total int default 0;
sum:loop
if n<=0 then
leave sum; -- 退出循环
end if;
if n%2 = 1
then set n := n - 1;
iterate sum; -- 进入下一次循环
end if;
set total :=total + n;
set n :=n-1;
end loop sum;
select total;
end;
call p10(100)
(7)游标
-- 查询游标
DECLARE 游标 CURSOR FOR 查询语句;
-- 打开游标
open 游标名称;
-- 获取游标记录
FETCH 游标名称 INTO 变量;
-- 关闭游标
CLOSE 游标名称;
-- 逻辑: -- A. 声明游标, 存储查询结果集 -- B. 准备: 创建表结构 -- C. 开启游标 -- D. 获取游标中的记录 -- E. 插入数据到新表中
-- F. 关闭游标
存储函数是有返回值的存储过程,存储过程的参数只能是IN类型的。具体语法如下:
characteristic说明:
create function 存储函数名称(参数列表)
returns type [characteristic]
begin
-- mysql语句
return ...;
end;
例:
create function fun1(n int)
returns int deterministic
begin
declare total int default 0;
while n>0 do
set total := total + n;
set n := n - 1;
end while;
return total;
end;
select fun1(50);
触发器是与表有关的数据库对象,是指在insert/update/delete之前(before)或之后(after),触发并执行触发器中定义的SQL语句的集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性, 日志记录 , 数据校验等操作。
使用别名OLD和NEW来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还只支持行级触发,不支持语句级触发。
触发器类型 | NEW 和 OLD |
---|---|
INSERT 型触发器 | NEW 表示将要或者已经新增的数据 |
UPDATE 型触发器 | OLD 表示修改之前的数据 , NEW 表示将要或已经修改后的数据 |
DELETE 型触发器 | OLD 表示将要或者已经删除的数据 |
CREATE TRIGGER trigger_name
BEFORE/AFTER INSERT/UPDATE/DELETE
ON tbl_name FOR EACH ROW -- 行级触发器
BEGIN
trigger_stmt;
END;
-- 查看触发器
show TRIGGERS;
-- 删除触发器
drop trigger [数据库名.]trigger_name; -- 没有指定数据库名 默认为当前数据库
create trigger tb_user_insert_trigger
after insert on tb_user for each row
begin
insert into user_logs(id, operation, operate_time,operate_id, operate_params) VALUES(null, 'insert', now(), new.id, concat('插入的数据内容为: id=',new.id,',name=',new.name, ', phone=', NEW.phone, ', email=', NEW.email, ', profession=', NEW.profession));
end;
DROP TRIGGER IF EXISTS status_change
#设置新的结束标记
DELIMITER $$
CREATE TRIGGER status_change AFTER UPDATE
ON sales_orders FOR EACH ROW
BEGIN
DECLARE total INT DEFAULT 0;
set total=total+1;
IF new.status = 3 AND old.status!=3
THEN
INSERT INTO stockout_lists
SELECT
*
FROM
sales_order_items
WHERE order_id = new.id ;
ELSEIF new.status = - 2
THEN
DELETE
FROM
stockout_lists
WHERE order_id = new.id;
END IF ;
-- SELECT total
UPDATE users set total -- 测试
END $$
DELIMITER ;
-- --------------------------------------
SELECT id,status FROM sales_orders ;
UPDATE
sales_orders
SET
STATUS = 3
WHERE id = 107 ;
UPDATE
sales_orders
SET
STATUS = 3
WHERE id = 149 ;
UPDATE
sales_orders
SET
STATUS = - 2
WHERE id = 9 ;
锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除了传统的计算机资源(CPU、RAM、I/O)的争用以外,数据也是一种提供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来首,锁数据库县的尤为重要。
全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。
典型的使用场景是做全库的逻辑备份,对所有表进行锁定,从而索取一致性视图,保证数据的完整性。
如图
此时备份出来的数据,是存在问题的。库存表和订单表中的数据不一致(有新的订单,但是库存数没减少)。
对数据库进行逻辑备份之前,先对数据库加上全局锁,之后的删除修改操作,就处于阻塞状态但是可以执行查询语句,也就是处于只读状态,而数据库备份就是查询语句。那么数据在进行逻辑备份的过程中,数据库的数据是不会发生变化的,就保证的了数据的一致性和完整性。
1 DDL:Data Definition Language,数据定义语言,用来维护存储数据的结构(数据库、表),代表指令为create、drop和alter等。
2 DML:Data Manipulation Language,数据操作语言,用来对数据进行操作(表中的内容)代表指令为insert、delete和update等,不过在 DML 内部又单独进行了一个分类,即 DQL(Data Query Language)数据查询语言,代表指令为select.
3 DCL:Data Control Language,数据控制语言,主要是负责(用户)权限管理,代表指令为grant和revoke等。
语法
-- 加全局锁
flush tables with read lock
-- 释放锁
unlock tables
特点
在InnoDB引擎中,可以在备份时加上参数来完成不加锁的一致性问题。
表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM,InnoDB,BDB等存储引擎中。
表级索主要分为以下三类:
语法
-- 加表锁
lock tables 表名 read/write
-- 释放锁
unlock tables
特点
1、读锁
对指定表加了读锁,不会影响其他表的读,但是会阻塞右侧客户端的写
2、写锁
对指定表加了写锁,会阻塞其他表的读和写
结论:读锁不会影响其他的读,但是会阻塞写。写锁既会阻塞其他的读和写
meta data lock 元数据锁,简写MDL。这里的元数据,可以简单理解成就是一张表的结构。也就是说,某一张表涉及到未提交的事务时,是不能够修改这张表结构的。
MDL加锁过程是系统自动控制,无需显式使用,在访问表的时候会自动加上。MDL锁主要作用是维护表元数据的一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。为了避免DML与DDL冲突,保证读写的正确性。
MYSQL5.5中引入了MDL,当对一张表进行增删改查的时候,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)。
对应SQL | 锁类型 | 说明 |
---|---|---|
lock tables xxx read/write | SHARED_READ_ONLY/ | 加读锁或写锁 |
select、slect … lock in share mode | SHARED_READ | 与SHARED_READ、SHARED_WRITE兼容,与EXCLUSIVE互斥 |
insert,update,select … for update | SHARED_WRITE | 与SHARED_READ、SHARED_WRITE兼容,与EXCLUSIVE |
alter table … | SHARED_WRITE | 与其他的MDL |
为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入意向锁,使表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。
分类
一旦事务提交了,意向排他锁,都会自动释放。
行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。
InnoDB的数据是基于索引组织的,行锁是通过对索引项加锁来实现的,而不是对记录的加锁。对于行级锁主要分为以下几类
行锁
InnoDB实现了以下两种类型的行锁。
(1)表空间
表空间是InnoDB存储引擎逻辑结构的最高层,如果用了参数,每个表都会有一个空间(xxx.ibd),一个MYSQL实例可以对应多个表空间,用于存储多个表空间,用于存储记录、索引等数据。
(2)段
分为数据段,索引段,回滚段,Inno是索引组织表,数据段就是B+树的叶子节点,索引段为B+树的非叶子节点。段用来管理多个Extent(区)。
(3)区
区,表空间的单元结构,每个区的大小为1M。默认情况下,InnoDB存储引擎页大小为16K,即一个区中一共有64个连续的页。
(4)页
页,是InnDB存储管理的最小单元,每个页的大小为16Kb。为了保证页的连续性InnoDB存储引擎每次从磁盘申请4-5区。
(5)行
InnoDB的存储引擎数据是按行进行存放的。
在行中,默认有两个隐藏字段:
事务时一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功要么同时失败。
特性