目录
一、事务
二、视图
1 、视图概念
2、为什么要使用视图
3 、性能问题
4 、定义视图
5、查看视图
6、删除视图
三、索引
1、引入索引的问题
2、索引是什么
3、索引为什么选择b+树
一、事务
事务是什么?
事务是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
事务就是一组原子性的SQL查询,或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询。如果其中有任何一条语句因为崩溃或其他原因无法执行,那么所有的语句都不会执行。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。
为什么需要事务?
例如 :银行转账的问题,从一个账号扣款并使另一个账号入账,这两个操作要么都执行,要么都不执行。所以,应该把它们看出一个事务。
事务的四大特性ACID:(记住)
- 原子性(atomicity)
- 一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。
- 一致性(consistency)
- 数据库总是从一个一致性的状态转换到另外一个一致性的状态。
- 隔离性(isolation)
- 通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。
- 持久性(durability)
- 一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必。而且不可能有能做到100%的持久性保证的策略
隔离级别
在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。较低级别的隔离通常可以执行更高的并发,系统的开销也更低。
每种存储引擎实现的隔离级别不尽相同。如果熟悉其他的数据库产品,可能会发现某些特性和你期望的会有些不一样(但本节不打算讨论更详细的内容)。读者可以根据所选择的存储引擎,查阅相关的手册。
查看当前会话隔离级别:
SELECT @@SESSION.transaction_isolation;
查看系统隔离级别:
SELECT @@GLOBAL.transaction_isolation;
四种隔离级别
- READ UNCOMITTED(未提交读)
- 在 READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。
- 事务可以读取未提交的数据,这也被称为脏读〈Dirty Read)。(也就是在A事务内插入某行数据,B事务在插入后查询语句会显示出A插入的信息)。这个级别会导致很多问题,从性能上来说,READ UNCOMMITTED不会比其他的级别好太多,但却缺乏其他级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用。
- READ COMMITTED(提交读)
- 大多数数据库系统的默认隔离级别都是READ COMMITTED(但 MySQL不是)
- READCOMMITTED满足前面提到的隔离性的简单定义:一个事务开始时,只能“看见”已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。(也就是在A事务内插入某行数据,B事务在A事务提交commit之后会显示出A插入的信息)。这个级别有时候也叫做不可重复读(nonrepeatableread),因为两次执行同样的查询,可能会得到不一样的结果。
- REPEATABLE READ(可重复读)
- REPEATABLE READ解决了脏读的问题。
- 该级别保证了在同一个事务中多次读取同样记录的结果是一致的。
- 但是理论上,可重复读隔离级别还是无法解决另外一个幻读(Phantom Read)的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(Phantom Row)。(也就是在A事务内插入某行数据,B事务在插入时查询语句和插入后查询语句是一样的,不会显示出A插入的信息,直到B事务关闭又重新读取才能读取到)。
- 可重复读是MySQL的默认事务隔离级别。
- SERIALIZABLE(可串行化)
- SERIALIZABLE是最高的隔离级别。
- 它通过强制事务串行执行,避免了前面说的幻读的问题。
- 简单来说,SERIALIZABLE 会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。实际应用中也很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。(也就是AB事务不允许同时执行)。
设置隔离级别
# 设置全局隔离级别
set global transaction isolation level REPEATABLE READ;
set global transaction isolation level READ COMMITTED;
set global transaction isolation level READ UNCOMMITTED;
set global transaction isolation level SERIALIZABLE;
#设置会话隔离级别
set session transaction isolation level REPEATABLE READ;
set session transaction isolation level READ COMMITTED;
set session transaction isolation level READ UNCOMMITTED;
set session transaction isolation level SERIALIZABLE;
开始事务,命令如下:
开启事务后执行修改命令,变更会维护到本地缓存中,而不维护到物理表中
begin;
#或者
start transaction;
提交事务,命令如下 将缓存中的数据变更维护到物理表中
commit;
回滚事务,命令如下:
rollback;
回滚撤回从"begin"开始的所有处理
二、视图
1 、视图概念
视图(View)是一种虚拟存在的表,对于使用视图的用户来说基本上是透明的。视图并不在数据库 中实际存放数据,它的数据来自定义视图时使用的基本表,并且是在使用视图时动态生成的。
2、为什么要使用视图
- 简化复杂的sql操作,在编写查询后,可以方便的重用它而不必知道它的查询细节。
- 重复使用该sql语句。
- 使用表的组成部分而不是整个表。
- 保护数据,可以给用户授予表的特定部分的访问权限而不是整个表。
- 更改数据格式和表示。
3 、性能问题
因为视图不包含数据,所以每次使用视图时,都必须处理查询执行时所需的任一个检索。如果是多个联结和过滤创建了复杂的视图或者嵌套了视图,可能会出现性能下降。
4 、定义视图
定义视图建议以"_v"开头
create view 视图名 as select语句;
5、查看视图
show tables; 默认会显示表和视图,但不能区分。
show full tables; 会显示表和视图的类型
6、删除视图
drop view viewname
三、索引
1、引入索引的问题
在图书馆查找一本书的过程。 在一般的软件系统中,对数据库的操作还是以查询为主,当数据量较大时,优化查询是关键。
那么可以给所有的列都加上索引嘛?(面试题)
其实不可以的,因为每添加一个索引就需要重新添加一颗b+树,如果所有的列都添加索引会导致插入和查询的成本变高,因此只需要在经常使用的列添加索引就是最好的。
2、索引是什么
索引是一种特殊的文件,它包含着对数据表里所有记录的引用指针。简单讲,就像一本书前面的目录, 能加快查询速度。
索引(在MySQL中也叫做“键(key)”) 是存储引擎用于快速找到记录的一种数据结构。这是索引的基本功能。
索引对于良好的性能非常关键。尤其是当表中的数据量越来越大时,索引对性能的影响愈发重要。在数据量较小且负载较低时,不恰当的索引对性能的影响可能还不明显,但当数据量逐渐增大时,性能则会急剧下降。
索引优化应该是对查询性能优化最有效的手段了。索引能够轻易将查询性能提高几个数量级,“最优”的索引有时比一个“好的”索引性能要好两个数量级。创建一个真正“最优”的索引经常需要重写查询。
- 索引是帮助mysql高效获取数据的数据结构
- 索引存储在文件系统中
- 索引的文件存储形式与存储引擎有关
- 索引文件的结构
3、索引为什么选择b+树
可以考虑作为索引的数据结构有如下几种:
- hash表
- 二叉树
- b树
- b+树(innoDB存储引擎上)使用b+树是因为b+树的高度稳定
使用hash表的缺点:
- hash存储需要将所有的数据文件添加到内存,浪费空间
- 如果是等值查询,hash很快,但实际工作中范围查找更多,而不是等值查询,所以hash就不合适了
测试索引
1)在mysql中创建数据库 test_indexdb,操作如下:
create database test_indexdb;
2)在test_indexdb中创建表 test_index,操作如下:
use test_indexdb; create table test_index(title varchar(20));
3)运行程序向表中插入1万条数据,都是字符串, c程序如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <mysql/mysql.h> int main() { MYSQL mysql_conn;//连接数据库句柄 MYSQL * mysql = mysql_init(&mysql_conn);//初始化句柄mysql_conn if ( mysql == NULL ) { printf("init err\n"); exit(1); } //连接数据库 mysql=mysql_real_connect(mysql,"localhost","root","Abc_111111","test_indexdb",3306,NUL L,0);//连接本机localhost,用户为root,密码Abc_111111,数据库test_indexdb,端口3306 if ( mysql == NULL ) { printf("connect err\n"); exit(1); } char sql_buff[128] = {0}; for( int i = 0;i < 10000; i++ ) { sprintf(sql_buff,"insert into test_index values('test-%d')",i);//待处理的执行语句存放入sql_buff内 if ( mysql_query(mysql,sql_buff) != 0 )//执行sql语句mysql_query() { printf("insert into err:%s\n",mysql_error(mysql));//打印错误信息 break; } } mysql_close(mysql); }
4) 查询验证 开启运行时间监测:
set profiling=1;
查找一条数据 : test-9999
select * from test_index where title='test-9999';
查看执行的时间:
show profiles;
为表test_index的title列创建索引:
create index title_index on test_index(title(20));
执行查询语句后,再次查看执行时间
select * from t_index where title='test-9999';
show profiles;
mysql> show profiles;
删除索引
drop index t_index on test_index;