1、事务 1.1、什么是事务? 一个事务其实就是一个完整的业务逻辑。 什么是一个完整的业务逻辑呢? 假设转账,从A账户向B账户中转账10000, 将A账户的钱减去10000(update语句) 将B账户的钱加上10000(update语句) 这就是一个完整的业务逻辑。 以上的操作是一个最小的工作单元,要么同时成功,要么同时失败,不可再分。 这两个update语句必须要求同时成功或者同时失败,这样才能保证钱是正确的。 1.2、只有DML语句才会有事务这一说法,其它语句和事务无关!!! insert delete update 只有以上的三个语句和事务有关系,其它都没有关系。 因为只有以上的三个语句是数据库表中数据进行增、删、改的。 只要你的操作一旦涉及到数据的增、删、改,那么就一定要考虑安全问题。 数据安全第一位!!! 1.3、假设所有的业务,只要一条DML语句就能完成,还有必要存在事务机制吗? 正是因为做某件事的时候,需要多条DML语句共同联合起来才能完成。 所以需要事务的存在。如果任何一件复杂的事都能一条DML语句搞定, 那么事务则没有存在的价值了。 到底什么是事务呢? 说到底,说到本质上,一个事务其实就是多条DML语句同时成功,或者同时失败! 事务:就是批量的DML语句同时成功,或者同时失败! 1.4、事务是怎么做到同时成功和同时失败的呢? InnoDB存储引擎:提供一组用来记录事务性活动的日志文件 事务开启了: insert insert insert delete update update update 事务结束了! 在事务的执行过程中,每一条DML的操作都会记录到"事务性活动的日志文件"中, 在事务的执行过程中,我们可以提交事务,也可以回滚事务。 提交事务? 清空事务性活动的日志文件,将数据全部彻底持久化到数据库中。 提交事务标志着,事务的结束。并且是一种全部成功的结束。 回滚事务? 将之前所有的DML操作全部撤销,并且清空事务性活动的日志文件 回滚事务标志着,事务的结束,并且是一种全部失败的结束。 1.5、怎么提交事务,怎么回滚事务? 提交事务:commit;语句 回滚事务:rollback;语句(回滚永远只能是回滚到上一次的提交点!) 事务对应的英语单词是:transction 测试一下,在mysql当中默认的事务行为是怎么样的? mysql默认情况下是支持自动提交事务的。(自动提交) 什么是自动提交? 每执行一条DML语句,则提交一次! 这种自动提交实际上是不符合我们的开发习惯,因为一个业务 通常是需要多条DML语句共同执行才能完成的,为了保证数据的安全, 必须要求同时成功之后再提交,所以不能执行一条就提交一条。 怎么将mysql的自动提交机制关闭呢? 先执行这个命令:start transction; 1.6、事务包括4个特性? A:原子性 说明事务是最小的工作单元,不可再分。 C:一致性 所有事务要求,在同一个事务当中,所有操作必须同时成功,或者同时失败,以保证数据的一致性。 I:隔离性 A事务和B事务之间具有一定的隔离。 教室A和教室B之间有一道墙,这道墙就是隔离性。 A事务在操作一张表的时候,另一个事务B也操作这张表会怎么样? D:持久性 事务最终结束的一个保障。事务提交,就相当于将没有保存到硬盘上的数据 保存到硬盘上! 1.7、重点研究一下事务的隔离性!!! A教室和B教室中间有一道墙,这道墙可以很厚也可以很薄。这就是事务的隔离级别。 这道墙越厚,表示隔离级别就越高。 事务和事务之间的隔离级别有哪些呢? 4个级别 读未提交: read uncommitted(最低的隔离级别) (没有提交就读到了) 什么是读未提交? 事务A可以读取到事务B未提交的数据。 这种隔离级别存在的问题就是: 脏读现象!(Dirty Read) 我们称读到了脏数据。 这种隔离级别一般都是理论上的,大多数的数据库隔离级别都是二档起步! 读已提交:read committed (提交之后才能读到) 什么是读已提交? 事务A只能读取到事务B提交之后的数据。 这种隔离级别解决了什么问题? 解决了脏读现象。 这种隔离级别存在什么问题呢? 不可重复读取数据。 什么是不可重复读取数据呢? 在事务开启之后,第一次读到的数据是3条,当前事务还没有 结束,可能第二次再读取的时候,读到的数据是4条,3不等于4 称为不可重复读取。 这种隔离级别是比较真实的数据,每一次读到的数据是绝对的真实。 oracle数据库默认的隔离级别是: read commited 可重复读: repeatable read (提交之后也读不到,永远读取的都是刚开启事务中的数据) 什么是可重复读取? 事务A开启之后,不管是多久,每一次在事务A中读取到的数据 都是一致的。即使事务B已经将数据修改,并且提交了,事务A读取到 的数据还是没有发生改变,这就是可重复读。 可重复读解决了什么问题? 解决了不可重复读取数据。 可重复读存在的问题是什么? 可能会出现幻影读。 每一次读取到的数据都是幻象,不够真实! 早晨9点开始开启了事务,只要事务不结束,到晚上9点,读到的数据还是那样! 读到的是假象,不够绝对的真实。 mysql中默认的事务级别就是这个!!! 序列化/串行化:serialiable(最高的隔离级别) 这是最高的隔离级别,效率最低。解决了所有的问题。 这种隔离级别表示事务排队,不能并发! synchronized,线程同步(事务同步) 每一次读取到的数据都是最真实的,并且效率是最低的。 1.8、验证各种隔离级别
查看隔离级别:SELECT @@tx_isolation ±----------------+ | @@tx_isolation | ±----------------+ | REPEATABLE-READ | ±----------------+ mysql默认的隔离级别
被测试的表t_user 验证:read uncommited mysql> set global transaction isolation level read uncommitted; 事务A 事务B
use bjpowernode; use bjpowernode; start transaction; select * from t_user; start transaction; insert into t_user values(‘zhangsan’); select * from t_user;
验证:read commited mysql> set global transaction isolation level read committed; 事务A 事务B
use bjpowernode; use bjpowernode; start transaction; start transaction; select * from t_user; insert into t_user values(‘zhangsan’); select * from t_user; commit; select * from t_user;
验证:repeatable read mysql> set global transaction isolation level repeatable read; 事务A 事务B
use bjpowernode; use bjpowernode; start transaction; start transaction; select * from t_user; insert into t_user values(‘lisi’); insert into t_user values(‘wangwu’); commit; select * from t_user;
验证:serializable mysql> set global transaction isolation level serializable; 事务A 事务B
use bjpowernode; use bjpowernode; start transaction; start transaction; select * from t_user; insert into t_user values(‘abc’); select * from t_user;
2、索引(index) 2.1、什么是索引? 索引在数据库表的字段上添加,是为了提高查询效率存在的一种机制。 一张表的一个字段可以添加一个索引,当然,多个字段联合起来也可以添加索引, 索引相当于一本书的目录,是为了缩小扫描范围而存在的一种机制。 对于一本字典来说,查找某个汉字有两种方式: 第一种方式:一页一页挨着找,直到找到为止,这种查找方式属于全字典扫描。 效率比较低。 第二种方式:先通过目录(索引)去定位一个大概的位置,然后直接定位到这个位置, 做局域性扫描,缩小扫描的范围,快速的查找。这种查找方式属于通过索引检索,效率较高。 t_user id(idIndex) name(nameIndex) email(emailIndex) address(addressIndex)
1 zhangsan… 2 lisi 3 wangwu 4 zhaoliu 5 hanmeimei 6 jack select * from t_user where name=‘jack’; 以上的这条SQL语句会去name字段上扫描,为什么? 因为查询条件是:name=‘jack’ 如果name字段上没有添加索引(目录),或者说没有给name字段创建索引, MYSQL会进行全扫描,会将name字段上的每一个值都比对一遍。效率比较低 MYSQL在查询方面主要就是两种方式: 第一种方式:全表扫描 第二种方式:根据索引检索 注意: 在实际中,汉语字典前面的目录是排序的,按照a b c d e f …排序, 为什么排序? 因为只有排序了才会有区间查找这一说!(缩小扫描范围其实就是扫描整个区间罢了!) 在mysql数据库当中索引也是需要排序的,并且这个索引的排序和TreeSet数据结构相同。 TreeSet(TreeMap)底层是一个自平衡的二叉树! 在mysql当中索引是一个B-Tree数据结构。 遵循左小右大原则存放,采用中序遍历方式遍历取数据。 2.2、索引的实现原理? 假设有一张用户表:t_user t_user id(PK) name
100 zhangsan 120 lisi 99 wangwu 88 zhaoliu 101 jack 55 lucy 130 tom 注意1:在任何数据库当中主键上都会自动添加索引对象,id字段上自动有索引, 因为id是PK。另外在mysql当中,一个字段上如果有unique约束的话,也会自动创建索引对象 注意2:在任何数据库当中,任何一张表的任何一条记录在硬盘存储上都有一个硬盘的物理存储编号。 注意3:在mysql当中,索引是一个单独的对象,不同的存储引擎以不同的形式存在, 在MYISAM存储引擎中,索引存储在一个.MYI文件中。在InnoDB存储引擎中 索引存储在一个逻辑名称叫做tablespace的当中。在MEMORY存储引擎当中索引 被存储在内存当中。不管索引存储在哪里,索引在mysql当中都是一个树的形式存在。(自平衡二叉树 B-tree) 2.3、在mysql当中,主键上,以及unique字段上都会自动添加索引的!!! 什么条件下,我们会考虑给字段添加索引呢? 条件1:数据量庞大(到底有多么庞大算庞大,这个需要测试,因为每一个硬件环境不同) 条件2:该字段经常出现在where的后面,以条件的形式存在,也就是说这个字段总是被扫描。 条件3:该字段很少的DML操作(insert delete alter)。(因为DML之后,索引需要重新排序) 建议不要随意添加索引,因为索引也是需要维护的,太多的话反而会降低系统的性能。 建议通过主键查询,建议通过unique约束的字段进行查询,效率是比较高的。 2.4、索引怎么创建?怎么删除?语法是什么? 创建索引:create index emp_ename_index on emp(ename); 给emp表的ename字段添加索引,起名:emp_ename_index 删除索引:drop index emp_ename_index on emp; 将emp表上的emp_ename_index索引对象删除。 2.5、在mysql当中,怎么查看一个SQL语句是否使用了索引进行检索? explain select * from emp where ename=‘king’; ±—±------------±------±-----------±-----±----------------±----------------±--------±------±-----±---------±------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | ±—±------------±------±-----------±-----±----------------±----------------±--------±------±-----±---------±------+ | 1 | SIMPLE | emp | NULL | ref | emp_ename_index | emp_ename_index | 43 | const | 1 | 100.00 | NULL | ±—±------------±------±-----------±-----±----------------±----------------±--------±------±-----±---------±------+ 如果没有使用索引的话,type=All 底层采用数组+树的结合体 2.6、索引有失效的时候,什么时候索引失效呢?
失效的第1种情况:
select * from emp where ename like '%T';
ename上即使添加了索引,也不会走索引,为什么?
原因是因为模糊匹配当中以“%”开头了!
尽量避免模糊查询的时候以“%”开始。
这是一种优化的手段/策略。
mysql> explain select * from emp where ename like '%T';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
失效的第2种情况:
使用or的时候会失效,如果使用or那么要求or两边的条件字段都要有
索引,才会走索引,如果其中一边有一个字段没有索引,那么另一个
字段上的索引也会实现。所以这就是为什么不建议使用or的原因。
mysql> explain select * from emp where ename = 'KING' or job = 'MANAGER';
+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | emp_ename_index | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+
失效的第3种情况:
使用复合索引的时候,没有使用左侧的列查找,索引失效
什么是复合索引?
两个字段,或者更多的字段联合起来添加一个索引,叫做复合索引。
create index emp_job_sal_index on emp(job,sal);
mysql> explain select * from emp where job = 'MANAGER';
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+
| 1 | SIMPLE | emp | ref | emp_job_sal_index | emp_job_sal_index
mysql> explain select * from emp where sal = 800; ±—±------------±------±-----±--------------±-----±--------±-----±-----±------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ±—±------------±------±-----±--------------±-----±--------±-----±-----±------------+ | 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where | ±—±------------±------±-----±--------------±-----±--------±-----±-----±------------+
失效的第4种情况:
在where当中索引列参加了运算,索引失效。
mysql> create index emp_sal_index on emp(sal);
explain select * from emp where sal = 800;
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
| 1 | SIMPLE | emp | ref | emp_sal_index | emp_sal_index | 9 | const | 1 | Using where |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
mysql> explain select * from emp where sal+1 = 800;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
失效的第5种情况:
在where当中索引列使用了函数
explain select * from emp where lower(ename) = 'smith';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Ext
2.7、索引是各种数据库进行优化的重要手段。优化的时候优先考虑的因素就是索引。 索引在数据库当中分了很多类? 单一索引:一个字段上添加索引。 复合索引:两个字段或者更多的字段上添加索引。 主键索引:主键上添加索引。 唯一性索引:具有unique约束的字段上添加索引。 … 注意:唯一性比较弱的字段上添加索引用处不大。
|