在进行事务讲述之前,想着提一下mysql的视图,存储过程,触发器。
Mysql视图(只针对单表)语法:create view (自定义table) as (sql语句); select * from table (where条件可加)
特点:1.无法提升性能 仅仅是简化sql语句;
2.提高安全性 :不暴露数据库重要字段
3.降低耦合
适合项目:OA / ERP 不适合电商项目
缺点:不易维护
触发器 类似于框架事件
Create trigger [触发器名称][触发器执行时间] on [表] for each row [函数或动作]
触发器执行时间:before,after
执行动作点: insert,update,delete
存储过程 ----- 一段SQL语句的集合 PHP的方法 实现业务逻辑 是事先编译好并存储在数据库中
Create procedure 存储过程名称(参数列表)
Begin
业务逻辑区域
End;
Call 存储过程名称(参数列表)
Drop procedure 存储过程名称(参数列表)
言简意赅,总之如无必要不建议使用,反正马爸爸的阿里是坚决不让用存储过程的...
下面进入正题:
谈起事务 首先要清楚事务的ACID四大特性:
原子性:事务是一系列操作,要么全部执行 要么全部不执行
备注:1、事务本身并不会自动回滚 需要手动去 rollback
2、在 MySQL 中,恢复机制是通过回滚日志(undo log)实现的,所有事务进行的修改都会先记录到这个回滚日志中,然
后在对数据库中的对应行进行写入。
一致性:事务在执行之前和执行之后都必须处于一致性状态
隔离性:当多个用户并发访问数据库时多个事务要互相隔离
在并发下事务会容易出现的一些问题:
数据更新丢失:两个事务同时操作一条数据,一个事务因为异常导致数据更新丢失
脏读 :一个失误开始读取了某行数据,另外一个事务已经更新了此数据但没有能够及时提交。这是相当危险的,因为很可能所有的操作都被回滚。
不可重复读:一个事务对同一行数据重复读取两次,但是却得到了不同的结果。例如,在两次读取的中途,有另外一个事务对该型数据进行了修改,并提交。
幻读:事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据(SQL不一定一样)。这是因为在两次查询过程中有另外一个事务插入数据
在MySQL中存在(InnoDB)事务存在着4中隔离级别,不同的隔离级别对事务的处理不同。
未授权读取(未提交读 Read Uncommitted):READ-UNCOMMITTED | 0:存在脏读,不可重复读,幻读的问题。如果一个事务已经开始写数据,则另外一个数据则不会允许同时进行写操作,但允许其他事务读此行数据。隔离级别可以通过“排他写锁”实现。
授权读取(已读提交 Read committed):READ-COMMITTED | 1:解决脏读的问题,存在不可重复读,幻读的问题。这个可以通过“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
可重复读取(Repeatable Read):REPEATABLE-READ | 2:解决脏读,不可重复读的问题,存在幻读的问题,默认隔离级别。可通过“共享锁”,“排他锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
序列化(Serializable):SERIALIZABLE | 3:解决脏读,不可重复读,幻读,可保证事务安全,但完全串行执行,性能最低。提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须要通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
可以通过 show variables like '%iso%';查看当前的隔离级别
持久性:一个事务一旦被提交,那么对数据库的数据改变就是永久性的
与原子性一样,事务的持久性也是通过日志来实现的,MySQL 使用重做日志(redo log)实现事务的持久性,重做日志由两部分组成,一是内存中的重做日志缓冲区,因为重做日志缓冲区在内存中,所以它是易丢失的,另一个就是在磁盘上的重做日志文件,它是持久的。
锁机制:
锁类型主要分为表级锁、行级锁、页级锁。
我们熟悉的 MyISAM存储引擎用的就是表级锁, 而目前最主流的 InnoDB存储引擎既支持表级锁也支持行级锁,默认用的是行级锁。
表级锁和行级锁有啥区别与联系呢?
1、表级锁,它直接锁住的是一个表,开销小,加锁快,不会出现死锁的情况,锁定粒度大,发生锁冲突的概率更高,并发度最低。
2、行级锁,它直接锁住的是一条记录,开销大,加锁慢,发生锁冲突的概率较低,并发度很高。
3、如果sql查询条件是走索引查询那么就是行级锁,反之就是表级锁。
InnoDB行级锁又分为共享锁和排他锁:
共享锁:select * from table_name lock in share mode;
排他锁:select * from table_name for update; (增删改操作下存储引擎会默认给当前数据增加排他锁,以防止出现死锁情况)
总结:排他锁常配合事务使用,用于处理高并发请求,比如秒杀操作等。弊端是可能会导致阻塞,因此关于秒杀程序多使用redis的事务+乐观锁代替,或者redis的队列都是非常优秀的,这个后续会讲。