MySQL 事务

MySQL 事务

前言

在我们平常的开发过程中,我们经常对于一个业务流程需要执行一组SQL,但是为了确保这一组SQL要么全部执行成功,要么全部不执行,我们需要用到MySQL的事务,而在使用事务的时候我先学习了一下自定义函数和储存过程。

自定义函数

MySQL是一个非常强大的数据库软件,它除了包含许多内置函数以外,还支持自定义编写函数,以便扩展更多的功能。

  • 语法格式

    CREATE FUNCTION <函数名> ( [ <参数1> <类型1> [,<参数2> <类型2>] ] … )
      RETURNS <类型>
      <函数主体>
    
    • <函数名>:指定自定义函数的名称。注意,自定义函数不能与存储过程具有相同的名称。
    • <参数><类型>:用于指定自定义函数的参数。这里的参数只有名称和类型,不能指定关键字 IN、OUT 和 INOUT。
    • RETURNS<类型>:用于声明自定义函数返回值的数据类型。其中,<类型>用于指定返回值的数据类型。
    • <函数主体>:自定义函数的主体部分,也称函数体。所有在存储过程中使用的 SQL 语句在自定义函数中同样适用,包括前面所介绍的局部变量、SET 语句、流程控制语句、游标等。除此之外,自定义函数体还必须包含一个 RETURN<值> 语句,其中<值>用于指定自定义函数的返回值。

    在 RETURN VALUE 语句中包含 SELECT 语句时,SELECT 语句的返回结果只能是一行且只能有一列值。

  • 注意

    MySQL的有个参数log_bin_trust_function_creators,当二进制日志启用后,这个变量就会启用。它控制是否可以信任存储函数创建者,不会创建写入二进制日志引起不安全事件的存储函数。如果设置为0(默认值),用户不得创建或修改存储函数,除非它们具有除CREATE ROUTINEALTER ROUTINE特权之外的SUPER权限。 设置为0还强制使用DETERMINISTIC特性或READS SQL DATANO SQL特性声明函数的限制。 如果变量设置为1,MySQL不会对创建存储函数实施这些限制。 此变量也适用于触发器的创建。

    # 查询
    show variables like 'log_bin_trust_function_creators';
    # 设置
    set global log_bin_trust_function_creators=1;
    # 如上设置只存在于当前操作,想要永久生效,需要写入到配置文件中: 
    #    在[mysqld]中加上 log_bin_trust_function_creators=1
    
  • 示例:随机产生字符串

    DELIMITER是设置分隔符(结尾符),默认MySQL的结尾符是;

    DELIMITER $$ 
    CREATE FUNCTION rand_string(n INT) 
      RETURNS VARCHAR(255) 
      BEGIN DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ'; 
      DECLARE return_str VARCHAR(255) DEFAULT ''; 
      DECLARE i INT DEFAULT 0; 
      WHILE i < n DO 
          SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));         
          SET i = i + 1; 
      END WHILE; 
      RETURN return_str; 
    END $$
    

存储过程

SQL语句需要先编译然后执行,而存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它。

存储过程的优点

(1)增强SQL语言的功能和灵活性:存储过程可以用控制语句编写,有很强的灵活性,可以完成复杂的判断和较复杂的运算。

(2)标准组件式编程:存储过程被创建后,可以在程序中被多次调用,而不必重新编写该存储过程的SQL语句。而且数据库专业人员可以随时对存储过程进行修改,对应用程序源代码毫无影响。

(3)较快的执行速度:如果某一操作包含大量的Transaction-SQL代码或分别被多次执行,那么存储过程要比批处理的执行速度快很多。因为存储过程是预编译的。在首次运行一个存储过程时查询,优化器对其进行分析优化,并且给出最终被存储在系统表中的执行计划。而批处理的ransaction-SQL语句在每次运行时都要进行编译和优化,速度相对要慢一些。

(4)减少网络流量:针对同一个数据库对象的操作(如查询、修改),如果这一操作所涉及的Transaction-SQL语句被组织进存储过程,那么当在客户计算机上调用该存储过程时,网络中传送的只是该调用语句,从而大大减少网络流量并降低了网络负载。

(5)作为一种安全机制来充分利用:通过对执行某一存储过程的权限进行限制,能够实现对相应的数据的访问权限的限制,避免了非授权用户对数据的访问,保证了数据的安全

  • 格式

    CREATE PROCEDURE  <过程名> ( [ <参数1> <类型1> [,<参数2> <类型2>] ] … )
      BEGIN
        [sql语句,sql语句]
      END
    

    过程名

    存储过程的名称,默认在当前数据库中创建。若需要在特定数据库中创建存储过程,则要在名称前面加上数据库的名称,即 db_name.sp_name。

    需要注意的是,名称应当尽量避免选取与 MySQL 内置函数相同的名称,否则会发生错误。

    参数

    存储过程根据需要可能会有输入、输出、输入输出参数,如果有多个参数用","分割开。MySQL存储过程的参数用在存储过程的定义,共有三种参数类型,IN,OUT,INOUT:

    IN参数的值必须在调用存储过程时指定,在存储过程中修改该参数的值不能被返回,为默认值

    OUT:该值可在存储过程内部被改变,并可返回

    INOUT:调用时指定,并且可被改变和返回

    过程体

    过程体的开始与结束使用BEGIN与END进行标识。

  • 示例 批量插入数据

    其中 rand_stringrand_num是自定义函数。

    DELIMITER $$
    CREATE PROCEDURE insert_emp( START INT , max_num INT)
      BEGIN 
            DECLARE i INT DEFAULT 0; 
            SET autocommit = 0; 
            REPEAT 
              SET i = i + 1; 
                INSERT INTO emp (empno, NAME ,age ,deptid ) VALUES ((START+i) ,rand_string(6), rand_num(30,50),rand_num(1,10000));
                UNTIL i = max_num 
            END REPEAT; 
            COMMIT; 
          END$$
    

事务

MySQL事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你既需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!

  • 在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
  • 事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行
  • 事务用来管理 insert,update,delete 语句。

一般来说,事务是必须满足4个条件(ACID)::原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。

  • 原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
  • 一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
  • 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
  • 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
  • 格式

    • 用 BEGIN, ROLLBACK, COMMIT来实现
      • BEGIN 开始一个事务
      • ROLLBACK 事务回滚
      • COMMIT 事务确认
    • 直接用 SET AUTOCOMMIT来改变 MySQL 的自动提交模式:
      • SET AUTOCOMMIT=0 禁止自动提交
      • SET AUTOCOMMIT=1 开启自动提交
  • 示例 命令行中使用事务

    mysql> select * from student;
    Empty set
    
    mysql> begin;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> insert into student(id,name) values(1,"tom");
    Query OK, 1 row affected (0.00 sec)
    
    mysql> insert into student(id,name) values(2,"selly");
    Query OK, 1 row affected (0.00 sec)
    
    mysql> commit;
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> select * from student;
    +----+-------+
    | id | name  |
    +----+-------+
    |  1 | tom   |
    |  2 | selly |
    +----+-------+
    2 rows in set (0.03 sec)
    

    在 MySQL 命令行的默认设置下,事务都是自动提交的,即执行 SQL 语句后就会马上执行 COMMIT 操作。因此要显式地开启一个事务务须使用命令 BEGIN 或 START TRANSACTION,或者执行命令 SET AUTOCOMMIT=0,用来禁止使用当前会话的自动提交。

并发事务遇到的问题

更新丢失(Lost Update)

当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题―一最后的更新覆盖了由其他事务所做的更新。

例如,两个程序员修改同一java文件。每程序员独立地更改其副本,然后保存更改后的副本,这样就覆盖了原始文档。最后保存其更改副本的编辑人员覆盖前一个程序员所做的更改。

如果在一个程序员完成并提交事务之前,另一个程序员不能访问同一文件,则可避免此问题。

脏读(Dirty Reads)

一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象地叫做”脏读”。

一句话:事务A读取到了事务B已修改但尚未提交的的数据,还在这个数据基础上做了操作。此时,如果B事务回滚,A读取的数据无效,不符合一致性要求。

不可重复读(Non-Repeatable Reads)

一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读”。

一句话:事务A读取到了事务B已经提交的修改数据,不符合隔离性

幻读(Phantom Reads)

一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。

一句话:事务A读取到了事务B体提交的新增数据,不符合隔离性。

多说一句:幻读和脏读有点类似,脏读是事务B里面修改了数据,幻读是事务B里面新增了数据。一句话:事务A读取到了事务B体提交的新增数据,不符合隔离性。多说一句:幻读和脏读有点类似,脏读是事务B里面修改了数据,幻读是事务B里面新增了数据。

事务隔离级别

脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的。同时,不同的应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对“不可重复读”和“幻读”并不敏感,可能更关心数据并发访问的能力。

查看当前数据库的事务隔离级别: show variables like 'tx_isolation";

事务隔离级别

具体可参考:事务隔离级别

参考文献

储存过程

你可能感兴趣的:(MySQL 事务)