mysql数据库之 存储引擎事务视图触发器存储过程函数流程控制

Posted 绝渊逢生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql数据库之 存储引擎事务视图触发器存储过程函数流程控制相关的知识,希望对你有一定的参考价值。

一、存储引擎

1.什么是存储引擎?

mysql中建立的库===>文件夹

库中建立的表===>文件

现实生活中我们用来存储数据的文件有不同的类型,每种文件类型对应各自不同的处理机制:比如处理文本用txt类型,处理表格用excel,处理图片用png等

数据库中的表也应该有不同的类型,表的类型不同,会对应mysql不同的存取机制,表类型又称为存储引擎。

存储引擎说白了就是如何存储数据、如何为存储的数据建立索引和如何更新、查询数据等技术的实现方
法。因为在关系数据库中数据的存储是以表的形式存储的,所以存储引擎也可以称为表类型(即存储和
操作此表的类型)

在Oracle 和SQL Server等数据库中只有一种存储引擎,所有数据存储管理机制都是一样的。而MySql
数据库提供了多种存储引擎。用户可以根据不同的需求为数据表选择不同的存储引擎,用户也可以根据
自己的需要编写自己的存储引擎

SQL 解析器、SQL 优化器、缓冲池、存储引擎等组件在每个数据库中都存在,但不是每 个数据库都有这么多存储引擎。MySQL 的插件式存储引擎可以让存储引擎层的开发人员设 计他们希望的存储层,例如,有的应用需要满足事务的要求,有的应用则不需要对事务有这 么强的要求 ;有的希望数据能持久存储,有的只希望放在内存中,临时并快速地提供对数据 的查询。

2.mysql支持的存储引擎

mysql>show engines\\G  #查看所有支持的存储引擎
mysql>show variables like \'storage_engine%\'; #查看正在使用的存储引擎

MySQL存储引擎介绍

#InnoDB 存储引擎
支持事务,其设计目标主要面向联机事务处理(OLTP)的应用。其
特点是行锁设计、支持外键,并支持类似 Oracle 的非锁定读,即默认读取操作不会产生锁。 从 MySQL 5.5.8 版本开始是默认的存储引擎。
InnoDB 存储引擎将数据放在一个逻辑的表空间中,这个表空间就像黑盒一样由 InnoDB 存储引擎自身来管理。从 MySQL 4.1(包括 4.1)版本开始,可以将每个 InnoDB 存储引擎的 表单独存放到一个独立的 ibd 文件中。此外,InnoDB 存储引擎支持将裸设备(row disk)用 于建立其表空间。
InnoDB 通过使用多版本并发控制(MVCC)来获得高并发性,并且实现了 SQL 标准 的 4 种隔离级别,默认为 REPEATABLE 级别,同时使用一种称为 netx-key locking 的策略来 避免幻读(phantom)现象的产生。除此之外,InnoDB 存储引擎还提供了插入缓冲(insert buffer)、二次写(double write)、自适应哈希索引(adaptive hash index)、预读(read ahead) 等高性能和高可用的功能。
对于表中数据的存储,InnoDB 存储引擎采用了聚集(clustered)的方式,每张表都是按 主键的顺序进行存储的,如果没有显式地在表定义时指定主键,InnoDB 存储引擎会为每一 行生成一个 6 字节的 ROWID,并以此作为主键。
InnoDB 存储引擎是 MySQL 数据库最为常用的一种引擎,Facebook、Google、Yahoo 等 公司的成功应用已经证明了 InnoDB 存储引擎具备高可用性、高性能以及高可扩展性。对其 底层实现的掌握和理解也需要时间和技术的积累。如果想深入了解 InnoDB 存储引擎的工作 原理、实现和应用,可以参考《MySQL 技术内幕:InnoDB 存储引擎》一书。

#MyISAM 存储引擎
**不支持事务、表锁设计、支持全文索引**,主要面向一些 OLAP 数 据库应用,在 MySQL 5.5.8 版本之前是默认的存储引擎(除 Windows 版本外)。数据库系统 与文件系统一个很大的不同在于对事务的支持,MyISAM 存储引擎是不支持事务的。究其根 本,这也并不难理解。用户在所有的应用中是否都需要事务呢?在数据仓库中,如果没有 ETL 这些操作,只是简单地通过报表查询还需要事务的支持吗?此外,MyISAM 存储引擎的 另一个与众不同的地方是,它的缓冲池只缓存(cache)索引文件,而不缓存数据文件,这与 大多数的数据库都不相同。

#NDB 存储引擎
2003 年,MySQL AB 公司从 Sony Ericsson 公司收购了 NDB 存储引擎。 NDB 存储引擎是一个集群存储引擎,类似于 Oracle 的 RAC 集群,不过与 Oracle RAC 的 share everything 结构不同的是,其结构是 share nothing 的集群架构,因此能提供更高级别的 高可用性。NDB 存储引擎的特点是数据全部放在内存中(从 5.1 版本开始,可以将非索引数 据放在磁盘上),因此主键查找(primary key lookups)的速度极快,并且能够在线添加 NDB 数据存储节点(data node)以便线性地提高数据库性能。由此可见,NDB 存储引擎是高可用、 高性能、高可扩展性的数据库集群系统,其面向的也是 OLTP 的数据库应用类型。

#Memory 存储引擎
正如其名,Memory 存储引擎中的数据都存放在内存中,数据库重启或发生崩溃,表中的数据都将消失。它非常适合于存储 OLTP 数据库应用中临时数据的临时表,也可以作为OLAP 数据库应用中数据仓库的维度表。Memory 存储引擎默认使用哈希 索引,而不是通常熟悉的 B+ 树索引。

#Infobright 存储引擎
第三方的存储引擎。其特点是存储是按照列而非行的,因此非常 适合 OLAP 的数据库应用。其官方网站是 http://www.infobright.org/,上面有不少成功的数据 仓库案例可供分析。

#NTSE 存储引擎
网易公司开发的面向其内部使用的存储引擎。目前的版本不支持事务, 但提供压缩、行级缓存等特性,不久的将来会实现面向内存的事务支持。

#BLACKHOLE
黑洞存储引擎,可以应用于主备复制中的分发主库。

MySQL 数据库还有很多其他存储引擎,上述只是列举了最为常用的一些引擎。如果 你喜欢,完全可以编写专属于自己的引擎,这就是开源赋予我们的能力,也是开源的魅 力所在。

3. 使用存储引擎

方法1:建表时指定

mysql>create table t1(id int,name char)engine=innodb;
mysql>create table t2(id int)engine=innodb;
mysql>show create table t1;
mysql>show create table t2;

方法2:在配置文件中指定默认的存储引擎

/etc/my.cnf
[mysqld]
default-storage-engine=INNODB
innodb_file_per_table=1

练习

创建四个表,分别使用innodb,myisam,memory,blackhole存储引擎,进行插入数据测试

mysql> create table t1(id int)engine=innodb;
mysql> create table t2(id int)engine=myisam;
mysql> create table t3(id int)engine=memory;
mysql> create table t4(id int)engine=blackhole;
mysql> quit
[root G:\\MYSQL\\data\\db3]# ls /var/lib/mysql/db1/ #发现后两种存储引擎只有表结构,无数据
db.opt  t1.frm  t1.ibd  t2.MYD  t2.MYI  t2.frm  t3.frm  t4.frm

#memory,在重启mysql或者重启机器后,表内数据清空
#blackhole,往表内插入任何数据,都相当于丢入黑洞,表内永远不存记录

二、事务

通俗的说,事务指一组操作,要么都执行成功,要么都执行失败;保证了对数据操作的数据安全性。

思考:
我去银行给朋友汇款,
我卡上有1000元,
朋友卡上1000元,
我给朋友转账100元(无手续费),
如果,我的钱刚扣,而朋友的钱又没加时,
网线断了,怎么办?

事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性

原子性(atomicity):原子意为最小的粒子,即不能再分的事务,要么全部执行,要么全部取消(就像上面的银行例子);一个事务是一个不可分割的工作单位。

一致性(consistency):指事务发生前和发生后,数据的总额依然匹配;事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。

隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。当事务完成后,其影响应该保留下来,不能撤消,只能通过“补偿性事务”来抵消之前的错误

演示:
			create table user (
				id int primary key auto_increment ,
				name varchar(32) not null default \'\',
				salary int not null default 0
			)charset utf8;
			
			insert into user (name, salary) values (\'zhang\', 1000);
			insert into user (name, salary) values (\'liu\', 1000);
			
		解决的方法:
			使用事务:
				start transaction;
					sql语句
				commit/rollback;
			
			例子:	
				commit成功:
				mysql> start transaction;
				Query OK, 0 rows affected (0.00 sec)

				mysql> update user set salary=900 where name=\'zhang\';
				Query OK, 1 row affected (0.01 sec)
				Rows matched: 1  Changed: 1  Warnings: 0

				mysql> select * from user;
				+----+-------+--------+
				| id | name  | salary |
				+----+-------+--------+
				|  1 | zhang |    900 |
				|  2 | liu   |   1000 |
				+----+-------+--------+
				2 rows in set (0.00 sec)

				mysql> update user set salary=1100 where name=\'zhang\';
				Query OK, 1 row affected (0.00 sec)
				Rows matched: 1  Changed: 1  Warnings: 0

				mysql> select * from user;
				+----+-------+--------+
				| id | name  | salary |
				+----+-------+--------+
				|  1 | zhang |    900 |
				|  2 | liu   |   1100 |
				+----+-------+--------+
				2 rows in set (0.00 sec)

				mysql> #2.提交
				mysql> commit;
				Query OK, 0 rows affected (0.06 sec)
				
				rollback回滚:
					mysql> start transaction;
					Query OK, 0 rows affected (0.00 sec)

					mysql>
					mysql>
					mysql> update user set salary=800 where name=\'zhang\';
					Query OK, 1 row affected (0.01 sec)
					Rows matched: 1  Changed: 1  Warnings: 0

					mysql> select * from user;
					+----+-------+--------+
					| id | name  | salary |
					+----+-------+--------+
					|  1 | zhang |    800 |
					|  2 | liu   |   1100 |
					+----+-------+--------+
					2 rows in set (0.00 sec)

					mysql> rollback;
					Query OK, 0 rows affected (0.11 sec)

					mysql> select * from user;
					+----+-------+--------+
					| id | name  | salary |
					+----+-------+--------+
					|  1 | zhang |    900 |
					|  2 | liu   |   1100 |
					+----+-------+--------+
					2 rows in set (0.00 sec)
				
				rollback回滚,影响所有:
					
					mysql> start transaction;
					Query OK, 0 rows affected (0.00 sec)
					
					mysql> update user set salary=800 where name=\'zhang\';
					Query OK, 1 row affected (0.00 sec)
					Rows matched: 1  Changed: 1  Warnings: 0

					mysql> update user set salary=700 where name=\'zhang\';
					Query OK, 1 row affected (0.00 sec)
					Rows matched: 1  Changed: 1  Warnings: 0

					mysql> select * from user;
					+----+-------+--------+
					| id | name  | salary |
					+----+-------+--------+
					|  1 | zhang |    700 |
					|  2 | liu   |   1100 |
					+----+-------+--------+
					2 rows in set (0.00 sec)

					mysql> rollback;
					Query OK, 0 rows affected (0.05 sec)

					mysql> select * from user;
					+----+-------+--------+
					| id | name  | salary |
					+----+-------+--------+
					|  1 | zhang |    900 |
					|  2 | liu   |   1100 |
					+----+-------+--------+
					2 rows in set (0.00 sec)

点此查看:mysql事务中的隔离级别

三、视图

1、什么是视图

​ 视图就是通过查询得到一张虚拟表,然后保存下来,下次直接使用即可

2、为什么要用视图

​ 如果要频繁使用一张虚拟表,可以不用重复查询

3、如何用视图

create view teacher2course as
select * from teacher inner join course on teacher.tid = course.teacher_id;

强调
1、在硬盘中,视图只有表结构文件,没有表数据文件
2、视图通常是用于查询,尽量不要修改视图中的数据

drop view teacher2course;

思考:开发过程中会不会去使用视图?

不会!视图是mysql的功能,如果你的项目里面大量的使用到了视图,那意味着你后期想要扩张某个功能的时候这个功能恰巧又需要对视图进行修改,意味着你需要先在mysql这边将视图先修改一下,然后再去应用程序中修改对应的sql语句,这就涉及到跨部门沟通的问题,所以通常不会使用视图,而是通过重新修改sql语句来扩展功能

四、触发器

在满足对某张表数据的增、删、改的情况下,自动触发的功能称之为触发器

为何要用触发器

​ 触发器专门针对我们对某一张表数据增insert、删delete、改update的行为,这类行为一旦执行
​ 就会触发触发器的执行,即自动运行另外一段sql代码

创建触发器语法

# 针对插入
create trigger tri_after_insert_t1 after insert on 表名 for each row
begin
    sql代码。。。
end 
create trigger tri_after_insert_t2 before insert on 表名 for each row
begin
    sql代码。。。
end

# 针对删除
create trigger tri_after_delete_t1 after delete on 表名 for each row
begin
    sql代码。。。
end
create trigger tri_after_delete_t2 before delete on 表名 for each row
begin
    sql代码。。。
end

# 针对修改
create trigger tri_after_update_t1 after update on 表名 for each row
begin
    sql代码。。。
end
create trigger tri_after_update_t2 before update on 表名 for each row
begin
    sql代码。。。
end

# 案例
CREATE TABLE cmd (
    id INT PRIMARY KEY auto_increment,
    USER CHAR (32),
    priv CHAR (10),
    cmd CHAR (64),
    sub_time datetime, #提交时间
    success enum (\'yes\', \'no\') #0代表执行失败
);

CREATE TABLE errlog (
    id INT PRIMARY KEY auto_increment,
    err_cmd CHAR (64),
    err_time datetime
);

delimiter $$  # 将mysql默认的结束符由;换成$$
create trigger tri_after_insert_cmd after insert on cmd for each row
begin
    if NEW.success = \'no\' then  # 新记录都会被MySQL封装成NEW对象
        insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);
    end if;
end $$
delimiter ;  # 结束之后记得再改回来,不然后面结束符就都是$$了

#往表cmd中插入记录,触发触发器,根据IF的条件决定是否插入错误日志
INSERT INTO cmd (
    USER,
    priv,
    cmd,
    sub_time,
    success
)
VALUES
    (\'li\',\'0755\',\'ls -l /etc\',NOW(),\'yes\'),
    (\'li\',\'0755\',\'cat /etc/passwd\',NOW(),\'no\'),
    (\'li\',\'0755\',\'useradd xxx\',NOW(),\'no\'),
    (\'li\',\'0755\',\'ps aux\',NOW(),\'yes\');

# 查询errlog表记录
select * from errlog;
# 删除触发器
drop trigger tri_after_insert_cmd;

五、存储过程

存储过程包含了一系列可执行的sql语句,存储过程存放于MySQL中,通过调用它的名字可以执行其内部的一堆sql

三种开发模型

第一种

"""
应用程序:只需要开发应用程序的逻辑
mysql:编写好存储过程,以供应用程序调用
优点:开发效率,执行效率都高
缺点:考虑到人为因素、跨部门沟通等问题,会导致扩展性差
"""

第二种

"""
应用程序:除了开发应用程序的逻辑,还需要编写原生sql
优点:比方式1,扩展性高(非技术性的)
缺点:
1、开发效率,执行效率都不如方式1
2、编写原生sql太过于复杂,而且需要考虑到sql语句的优化问题
"""

第三种

"""
应用程序:开发应用程序的逻辑,不需要编写原生sql,基于别人编写好的框架来处理数据,ORM
优点:不用再编写纯生sql,这意味着开发效率比方式2高,同时兼容方式2扩展性高的好处
缺点:执行效率连方式2都比不过
"""

创建存储过程

delimiter $$
create procedure p1(
    in m int,  # in表示这个参数必须只能是传入不能被返回出去
    in n int,  
    out res int  # out表示这个参数可以被返回出去,还有一个inout表示即可以传入也可以被返回出去
)
begin
    select tname from teacher where tid > m and tid < n;
    set res=0;
end $$
delimiter ;

如何用存储过程

# 大前提:存储过程在哪个库下面创建的只能在对应的库下面才能使用!!!

# 1、直接在mysql中调用
set @res=10  # res的值是用来判断存储过程是否被执行成功的依据,所以需要先定义一个变量@res存储10
call p1(2,4,10);  # 报错
call p1(2,4,@res);  

# 查看结果
select @res;  # 执行成功,@res变量值发生了变化

# 2、在python程序中调用
pymysql链接mysql
产生的游表cursor.callproc(\'p1\',(2,4,10))  # 内部原理:@_p1_0=2,@_p1_1=4,@_p1_2=10;
cursor.excute(\'select @_p1_2;\')


# 3、存储过程与事务使用举例(了解)
delimiter //
create PROCEDURE p5(
    OUT p_return_code tinyint
)
BEGIN
    DECLARE exit handler for sqlexception
    BEGIN
        -- ERROR
        set p_return_code = 1;
        rollback;
    END;


  DECLARE exit handler for sqlwarning
  BEGIN
      -- WARNING
      set p_return_code = 2;
      rollback;
  END;

  START TRANSACTION;
      update user set balance=900 where id =1;
      update user123 set balance=1010 where id = 2;
      update user set balance=1090 where id =3;
  COMMIT;

  -- SUCCESS
  set p_return_code = 0; #0代表执行成功


END //
delimiter ;

六、函数

注意与存储过程的区别,mysql内置的函数只能在sql语句中使用!

CHAR_LENGTH(str)
			返回值为字符串str 的长度,长度的单位为字符。一个多字节字符算作一个单字符。
			对于一个包含五个二字节字符集, LENGTH()返回值为 10, 而CHAR_LENGTH()的返回值为5。
		
		CONCAT(str1,str2,...)
			字符串拼接
			如有任何一个参数为NULL ,则返回值为 NULL。
		FORMAT(X,D)
			将数字X 的格式写为\'#,###,###.##\',以四舍五入的方式保留小数点后 D 位, 并将结果以字符串的形式返回。若  D 为 0, 则返回结果不带有小数点,或不含小数部分。
			例如:
				SELECT FORMAT(12332.1,4); 结果为: \'12,332.1000\'
		INSTR(str,substr)
			返回字符串 str 中子字符串的第一个出现位置。
		LEFT(str,len)
			返回字符串str 从开始的len位置的子序列字符。
		LOWER(str)
			变小写
		UPPER(str)
			变大写
		LTRIM(str)
			返回字符串 str ,其引导空格字符被删除。
		RTRIM(str)
			返回字符串 str ,结尾空格字符被删去。
		SUBSTRING(str,pos,len)
			获取字符串子序列
		LOCATE(substr,str,pos)
			获取子序列索引位置
		REPEAT(str,count)
			返回一个由重复的字符串str 组成的字符串,字符串str的数目等于count 。
			若 count <= 0,则返回一个空字符串。
			若str 或 count 为 NULL,则返回 NULL 。
		REPLACE(str,from_str,to_str)
			返回字符串str 以及所有被字符串to_str替代的字符串from_str 。
		REVERSE(str)
			返回字符串 str ,顺序和字符顺序相反。
		RIGHT(str,len)
			从字符串str 开始,返回从后边开始len个字符组成的子序列
		
		
		http://doc.mysql.cn/mysql5/refman-5.1-zh.html-chapter/functions.html#encryption-functions
CREATE TABLE blog (
    id INT PRIMARY KEY auto_increment,
    NAME CHAR (32),
    sub_time datetime
);

INSERT INTO blog (NAME, sub_time)
VALUES
    (\'第1篇\',\'2015-03-01 11:31:21\'),
    (\'第2篇\',\'2015-03-11 16:31:21\'),
    (\'第3篇\',\'2016-07-01 10:21:31\'),
    (\'第4篇\',\'2016-07-22 09:23:21\'),
    (\'第5篇\',\'2016-07-23 10:11:11\'),
    (\'第6篇\',\'2016-07-25 11:21:31\'),
    (\'第7篇\',\'2017-03-01 15:33:21\'),
    (\'第8篇\',\'2017-03-01 17:32:21\'),
    (\'第9篇\',\'2017-03-01 18:31:21\');

select date_format(sub_time,\'%Y-%m\'),count(id) from blog group by date_format(sub_time,\'%Y-%m\');

七、流程控制

# if条件语句
delimiter //
CREATE PROCEDURE proc_if ()
BEGIN
    
    declare i int default 0;
    if i = 1 THEN
        SELECT 1;
    ELSEIF i = 2 THEN
        SELECT 2;
    ELSE
        SELECT 7;
    END IF;

END //
delimiter ;
# while循环
delimiter //
CREATE PROCEDURE proc_while ()
BEGIN

    DECLARE num INT ;
    SET num = 0 ;
    WHILE num < 10 DO
        SELECT
            num ;
        SET num = num + 1 ;
    END WHILE ;

END //
delimiter ;

八、数据库备份(运维方向)

为啥要备份?
	将重要的数据保存下来
	用法:
		#语法:
		# mysqldump -h 服务器 -u用户名 -p密码 数据库名 表名,  表名,.... > aaa.sql

		#示例:
		#单库备份
		mysqldump -uroot -p123 db1 > db1.sql
		mysqldump -uroot -p123 db1 table1 table2 > db1-table1-table2.sql

		#多库备份
		mysqldump -uroot -p123 --databases db1 db2 mysql db3 > db1_db2_mysql_db3.sql

		#备份所有库
		mysqldump -uroot -p123 --all-databases > all.sql

	重新导入:
	create database test3;
	use test3;
	mysql> source D:/test3.sql;

以上是关于mysql数据库之 存储引擎事务视图触发器存储过程函数流程控制的主要内容,如果未能解决你的问题,请参考以下文章

MySQL之视图触发器事务存储过程函数

MySQL之视图触发器事务存储过程函数

Mysql之视图 触发器 事务 存储过程 函数

MySQL之视图触发器事务存储过程函数 流程控制

python数据库MySQL之视图,触发器,事务,存储过程,函数

python-day48--mysql之视图触发器事务存储过程函数