视图
视图介绍
视图(View)是一种虚拟存在的表,对于使用视图的用户来说基本上是透明的。视图并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。
我们之前学的要运行sql语句有对应的数据库,那么当数据库改变了,我们对应的sql语句也要有所修改。那么现在我们在不改变sql语句的前提下使用不同数据库名,那么视图就是这样的存在。
在sql语句与数据库直接联系变为间接联系。中间加入的就是视图。sql语句对应视图,由视图来映射不同的数据库。sql语句可以不用修改,只需要改变视图即可。
视图其实优势在于对若干张表的应用(用来查询)
方便查询操作检查复杂的sql语句,增强可读性
视图取名
一般我们视图的取名都是 v_ + 表名(连接的表名前称)【最好不要这样定义,容易见名知意】
直接v_ +一些名称即可(并非见名知意)
定义视图
语句:create view 视图名称 as select 语句
create view指创建试图
创建视图的来源在 as后面的select 语句
- 原始sql语句
SELECT * FROM provinces;
-视图连接表格
CREATE VIEW v_pro AS SELECT * FROM provinces;
- 通过执行视图,也可以再后面添加语句。减少了复杂语句,增加可读性。
SELECT * FROM v_pro;
查看视图
查看表的时候会把视图表也列出来
show tables;
使用视图
select * from v_pro;
删除视图
drop view 视图名称;
视图的作用
- 简单:提高了重用性,就像一个函数。
- 安全:提高了安全性能,可以针对不同的用户,设定不同的视图。
- 数据独立:一旦视图的结构确定了,可以屏蔽表结构变化对用户的影响,源表增加列对视图没有影响;源表修改列名,则可以通过修改视图来解决,不会造成对访问者的影响
视图的修改
有下列内容之一,视图不能做修改
- select子句中包含distinct
- select字句中包含组函数
- select语句中包含group by子句
- selecy语句红包含order by子句
- where子句中包含相关子查询
- from字句中包含多个表
- 如果视图中有计算列,则不能更新
- 如果基表中有某个具有非空约束的列未出现在视图定义中,则不能做insert操作。
事务
为什么要有事务
事务广泛的运用于订单系统、银行系统等多种场景
例如:
A用户和B用户是银行的储户,现在A要给B转账500元,那么需要做以下几件事:
检查A的账户余额>500元;
A 账户中扣除500元;
B 账户中增加500元;
正常的流程走下来,A账户扣了500,B账户加了500,皆大欢喜。
那如果A账户扣了钱之后,系统出故障了呢?A白白损失了500,而B也没有收到本该属于他的500。
以上的案例中,隐藏着一个前提条件:A扣钱和B加钱,要么同时成功,要么同时失败。事务的需求就在于此
事务
所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位
例如,银行转帐工作:从一个帐号扣款并使另一个帐号增款,这两个操作要么都执行,要么都不执行。所以,应该把他们看成一个事务。事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性
假如一个银行的数据库有两张表:支票表(checking)和储蓄表(savings)。现在要从用户Jane的支票账户转移200美元到她的储蓄账户,那么至少需要三个步骤:
1.检查支票账户的余额高于或者等于200美元。
2.从支票账户余额中减去200美元。
3.在储蓄帐户余额中增加200美元。
上述三个步骤的操作必须打包在一个事务中,任何一个步骤失败,则必须回滚所有的步骤。
事务四大特性(简称ACID) 【重要!!】
- 原子性(Atomicity)
- 一致性(Consistency)
- 隔离性(Isolation)
- 持久性(Durability)
原子性(Atomicity)
一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性
一致性(Consistency)
数据库总是从一个一致性的状态转换到另一个一致性的状态。(在前面的例子中,一致性确保了,即使在执行第三、四条语句之间时系统崩溃,支票账户中也不会损失200美元,因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中。回滚只有转账前和转账后两个状态。)
隔离性(isolation)
通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。(在前面的例子中,当执行完第三条语句、第四条语句还未开始时,此时有另外的一个账户汇总程序开始运行,则其看到支票帐户的余额并没有被减去200美元。)
持久性(durability)
一旦事务提交,则其所做的修改会永久保存到数据库。(此时即使系统崩溃,修改的数据也不会丢失。)
事务的状态
我们现在知道事务是一个抽象的概念,它其实对应着一个或多个数据库操作,设计数据库的大叔根据这些操作所执行的不同阶段把事务大致上划分成了这么几个状态:
活动的(active)
事务对应的数据库操作正在执行过程中时,我们就说该事务处在活动的状态。
部分提交的(partially committed)
当事务中的最后一个操作执行完成,但由于操作都在内存中执行,所造成的影响并没有刷新到磁盘时,我们就说该事务处在部分提交的状态。【即事务基本都完成了就差commit;代码提交了】
失败的(failed)
当事务处在活动的或者部分提交的状态时,可能遇到了某些错误(数据库自身的错误、操作系统错误或者直接断电等)而无法继续执行,或者人为的停止当前事务的执行,我们就说该事务处在失败的状态。【还没有回滚】
中止的(aborted)
如果事务执行了半截而变为失败的状态,当狗哥账户的钱被扣除,但是猫爷账户的钱没有增加时遇到了错误,从而当前事务处在了失败的状态,那么就需要把已经修改的狗哥账户余额调整为未转账之前的金额,换句话说,就是要撤销失败事务对当前数据库造成的影响。书面一点的话,我们把这个撤销的过程称之为回滚。当回滚操作执行完毕时,也就是数据库恢复到了执行事务之前的状态,我们就说该事务处在了中止的状态。【失败后回滚完成了】
提交的(committed)
当一个处在部分提交的状态的事务将修改过的数据都同步到磁盘上之后,我们就可以说该事务处在了提交的状态。
随着事务对应的数据库操作执行到不同阶段,事务的状态也在不断变化,一个基本的状态转换图如下所示
事务命令
表的引擎类型必须是innodb类型才可以使用事务,这是mysql表的默认引擎
- 开启事务,命令如下
开启事务后执行修改命令,变更会维护到本地缓存中,而不维护到物理表中【先放到缓存中】
start transaction;这个命令更加强大,因为后面可以添加其他代码,比如设为
只读事务:start transaction READ only;
读写事务:start transaction READ WRITE;
begin;
# 或者
start transaction;
- 提交事务,命令如下
将缓存中的数据变更维护到物理表中
commit;
- 回滚事务,命令如下
放弃缓存中变更的数据
rollback;
保存点
如果你开启了一个事务,并且已经敲了很多语句,忽然发现上一条语句有点问题,你只好使用ROLLBACK语句来让数据库状态恢复到事务执行之前的样子,然后一切从头再来,总有一种一夜回到解放前的感觉。所以设计数据库的大叔们提出了一个保存点(英文:savepoint)的概念,就是在事务对应的数据库语句中打几个点,我们在调用ROLLBACK语句时可以指定会滚到哪个点,而不是回到最初的原点。
SAVEPOINT 保存点名称(名字可以随意起);
当我们想回滚到某个保存点时,可以使用下边这个语句(下边语句中的单词WORK和SAVEPOINT是可有可无的):
ROLLBACK [WORK] TO [SAVEPOINT] 保存点名称;
不过如果ROLLBACK语句后边不跟随保存点名称的话,会直接回滚到事务执行之前的状态。
如果我们想删除某个保存点,可以使用这个语句:
RELEASE SAVEPOINT 保存点名称;
整体:
# 原始代码
UPDATE checking set money =401 WHERE ID =1;
# 打保存点
SAVEPOINT S1;
# 修改原始代码
UPDATE checking set money =402 WHERE ID =1;
# 打保存点
SAVEPOINT S2;
# 想要回到之前原始代码
ROLLBACK TO s1;
# 如果觉得可以了就提交commit;
注意
- 修改数据的命令会自动的触发事务,包括insert、update、delete
- 而在SQL语句中有手动开启事务的原因是:可以进行多次数据的修改,如果成功一起成功,否则一起会滚到之前的数据
事务练习-模拟银行转账
# 创建完表格。checking 和 savings
# 开启事务
START TRANSACTION; # 另一个方法: BEGIN;,但是第一个方法比较厉害
# 1、检查支票账户的余额高于或者等于200.
SELECT * FROM checking WHERE NAME ="jvran";
# 2.从支票账户余额中减去200
UPDATE checking money=moner-200 WHERE name="jvran";
# 3.在储蓄账户余额中增加200
UPDATE savings money=moner +200 WHERE name="jvran";
# 提交事务
COMMIT;
# 回滚(出现错误后再回滚到最开始的状态)
ROLLBACK;
事务隔离级别
隔离级别(ISOLATION LEVEL)
隔离性其实比想象要复杂。在SQL中定义了四种隔离的级别,每一种隔离级别都规定了一个事务中的修改,哪些是在事务内和事务间是可见的,哪些是不可见的。较低级别的隔离通常来说能承受更高的并发,系统的开销也会更小。
查看当前事物级别
SELECT @@tx_isolation;
设置mysql的隔离级别
基本语法
set session transaction isolation level 设置事务隔离级别
READ UNCOMMITTED(未提交读)
在READ UNCOMMITTED级别,事务的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read)。这个级别的隔离会导致很多问题,虽然在性能方面是最优的,但是缺乏其他级别的很多好处,所以这种隔离的级别很少在实际中应用。
READ UNCOMMITTED实践 开启两个MySQL SESSION,并将MySQL的默认隔离级别设置为READ UNCOMMITTED
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
READ COMMITTED(读已提交)
大多数数据库系统默认的隔离级别都是READ COMMITTED(但MySQL不是),"读已提交"简单的定义:一个事务只能看见已经提交的事务的修改结果。换句话说,一个事务从开启事务到提交事务之前,对其他事务都是不可见的,因此在同一个事务中的两次相同查询结果可能不一样。故这种隔离级别有时候也叫不可重复读(NONREPEATABLE READ)。
READ COMMITTED 实践
REPEATABLE READ(可重复读)
"可重复读"是MySQL的默认事务隔离级别。REPEATABLE READ解决了脏读的问题,该级别保证了在同一次事务中多次查询相同的语句结果是一致的。但是"可重复读"隔离级别无法避免产生幻行(Phantom Row)的问题,MySQL的InnoDB引擎通过多版本并发控制(MVCC,Multiversion Concurrency Controller)解决了幻读的问题。
REPEATABLE READ 产生幻行的实践
从上面GIF图显示的过程我们可以看到,最后SESSION A查询语句的结果只有一条id为1的数据,但是我们在插入id=2的数据的时候产生了报错
ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'
从报错中很容易就能看到是因为id=2的行已经存在了,前面读取行数据的结果就是幻读。
REPEATABLE READ 实践
SERIALIZABLE(可串行化)
SERIALIZABLE是最高的隔离级别,它通常通过强制事务串行,避免了前面说的幻读问题。简单来说,"可串行化"会在读取的每一行数据上都加锁,所以可能会导致大量的锁等待和超时问题,所以在实际的生产环境中也很少会用到这个隔离级别,只有在非常需要确保数据的一致性切可以接受没有并发的情况下,才会考虑使用这个隔离级别。
SERIALIZABLE实践
从上面的过程我们可以看到,"可串行化"是通过对每一行数据都加锁的方式来避免幻行问题,这种方式效率非常的低,很容易造成较长时间的锁等待。
对比
好的事务习惯
- 循环写入的情况,如果循环次数不是太多,建议在循环前开启一个事务,循环结束后统一提交。
- 优化事务里的语句顺序,减少锁时间。
- 创建事务之前,关注事务隔离级别。
- 不在事务中混合使用存储引擎。