1. Mysql 트랜잭션의 4가지 특징:
Atomicity , Consistent, Isalotion, Durable, ACID라고 함.
원자성: 트랜잭션의 원자성 작업, 데이터에 대한 모든 작업은 모두 성공하거나 모두 실패하며 트랜잭션의 원자성은 트랜잭션의 Redo/Undoh 메커니즘을 기반으로 합니다.
일관성: 트랜잭션이 실행되기 전과 실행된 후의 상태는 일관성이 있으며, 이는 데이터의 일관성으로 이해됩니다.
격리: 트랜잭션은 서로 격리되어 영향을 받지 않으며 트랜잭션의 격리 수준과 관련이 있습니다.
지속성: 트랜잭션이 커밋된 후 트랜잭션의 상태가 데이터베이스에 유지됩니다.
2. 다시 실행/실행 취소 메커니즘:
리두 로그: 데이터베이스에 기록되지 않았지만 트랜잭션이 성공적으로 커밋된 데이터를 복원하는 데 사용할 수 있습니다. (어느 시점에서 내 트랜잭션이 제출되었고 데이터베이스에 쓰려고 할 때 데이터베이스가 끊겼습니다. 이 때 데이터베이스가 다시 시작되면 Redo 로그를 통해 데이터가 복원됩니다.) Undo 로그: 데이터가 수정 되기
전에 기록하기 위해 사용 하는 값 주로 트랜잭션 실행 실패 후 롤백에 사용됩니다.
3. 트랜잭션 격리 수준:
1. 커밋되지 않은 읽기(READ UNCOMMITTED) , 더티 읽기 문제가 발생합니다.
2. READ COMMITTED 는 더티 읽기의 문제를 해결하고 반복할 수 없는 읽기가 발생합니다. 즉 트랜잭션에서 언제든지 읽은 데이터가 다를 수 있으며 데이터 수정 및 제출 후 다른 트랜잭션에 의해 영향을 받을 수 있습니다. 일반적으로 업데이트 작업용입니다.
3. 반복 읽기(REPEATABLE READ)는 이전의 반복 불가능한 읽기 및 더티 읽기의 문제를 해결하지만 일반적으로 삽입 작업에 대한 팬텀 읽기 문제를 가져옵니다.
4. 직렬화(SERIALIZABLE)
#总结
读未提交:允许读取尚未提交的数据变更。可能会导致脏读、幻读或不可重复读。
读已提交:允许读取已经提交的数据。可能会导致幻读和不可重复读。
可重复读:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改。可能会导致幻读。
可串行化:最高隔离级别
原子性:通过undolog实现。
持久性:通过redolog实现。
隔离性:通过加锁(当前读)&MVCC(快照读)实现。
一致性:通过undolog、redolog、隔离性共同实现。
Dirty read problem: 트랜잭션 A를 열어 데이터를 수정했지만 이 수정이 데이터베이스에 제출되지 않았습니다. 이때 다른 트랜잭션 B도 이 데이터에 액세스합니다. 트랜잭션 수정 후 제출되지 않은 데이터가 A는 더티 읽기라고 하는 읽기입니다.
비 반복 읽기 문제: 트랜잭션 내에서 동일한 데이터를 여러 번 읽어 결과가 다른 것을 의미합니다. 트랜잭션 A는 트랜잭션을 시작하고 데이터를 읽지만 트랜잭션을 제출하지는 않으며 이때 트랜잭션 B도 데이터에 대한 트랜잭션을 시작합니다.
그리고 이 데이터를 수정하여 트랜잭션을 제출했는데, 트랜잭션 A가 이 데이터를 다시 읽어보니 일관성 없는 결과가 나왔다.
예:
시간 | 트랜잭션 | B사 |
---|---|---|
1 | 트랜잭션 A 시작 | |
2 | 처음으로 데이터 읽기: num=1000 | |
삼 | 트랜잭션 시작 B | |
4 | 동일한 데이터 읽기: num=1000, num+100 | |
5 | num=1100, 트랜잭션 B 제출 | |
6 | 두 번째 읽기: num=1100, 더티 읽기 |
팬텀 읽기: 트랜잭션이 독립적으로 실행되지 않을 때 발생하는 현상을 말하며, 예를 들어 첫 번째 트랜잭션이 데이터 행 범위를 쿼리합니다. 동시에 두 번째 트랜잭션은 이 범위의 데이터를 추가, 수정 및 삭제하여 결과가 일치하지 않습니다. 이 테이블의 데이터는 범위 데이터에 대한 것입니다.
이 수정은 새 데이터 행을 테이블에 삽입하는 것입니다. 그러면 앞으로 첫 번째 트랜잭션을 운영하는 사용자는 착시가 일어난 것처럼 테이블에 여전히 수정되지 않은 데이터 행이 있음을 알게 될 것입니다.
예를 들면:
시간 | 트랜잭션 | B사 |
---|---|---|
1 | 트랜잭션 A 시작 | |
2 | 쿼리 테이블에서 Age=20, 총 1000개 항목: | |
삼 | 트랜잭션 시작 B | |
4 | 데이터 조각을 테이블에 삽입, age=20 | |
5 | 커밋 트랜잭션 B | |
6 | 두 번째 쿼리는 1001이 됩니다: 팬텀 읽기 |
비반복 읽기의 핵심은 다음과 같이 수정하는 것입니다.
동일한 조건, 읽은 데이터를 다시 읽어서 값이 다른지 확인
팬텀 읽기의 핵심은 추가하거나 삭제하는 것입니다.
동일한 조건 , 첫 번째 및 두 번째로 읽은 레코드 수가 다릅니다.
업데이트 손실 문제의 첫 번째
A 트랜잭션이 철회되면 제출된 B 트랜잭션의 업데이트된 데이터를 덮어씁니다.
예를 들어:
시간 | 트랜잭션 | B사 |
---|---|---|
1 | 트랜잭션 A 시작 | |
2 | 트랜잭션 시작 B | |
삼 | 쿼리 데이터 번호=1000 | |
4 | 쿼리 데이터 번호=1000 | |
5 | 수정: 숫자+100 | |
6 | num=1100, 트랜잭션 B 제출 | |
7 | 수정: num-100 | |
8 | 수정에 실패하면 트랜잭션이 롤백됩니다. |
최종 롤백 결과는 num=1000이고 트랜잭션 A의 롤백으로 인해 트랜잭션 B가 제출한 데이터가 손실되었습니다.
업데이트 손실 문제의 두 번째 유형:
트랜잭션이 B 트랜잭션이 제출한 데이터를 덮어쓰면 B 트랜잭션이 수행한 작업이 손실됩니다.
시간 | 트랜잭션 | B사 |
---|---|---|
1 | 트랜잭션 A 시작 | |
2 | 트랜잭션 시작 B | |
삼 | 쿼리 데이터 번호=1000 | |
4 | 쿼리 데이터 번호=1000 | |
5 | 수정: 숫자+100 | |
6 | num=1100, 트랜잭션 B 제출 | |
7 | 수정: num-100 | |
8 | 커밋 트랜잭션: num=900 |
최종 num 결과는 900이며, A 트랜잭션의 커밋이 B 트랜잭션을 커버하므로 B 트랜잭션의 데이터 손실이 발생합니다.
데이터베이스 기본 격리 수준: 반복 읽기(REPEATABLE READ) , 격리 수준이 높을수록 동시성이 낮아집니다.
격리 수준 | 더러운 읽기 | 반복 불가능한 읽기 | 팬텀 판독 | 첫 번째 유형의 업데이트가 손실됨 | 두 번째 유형의 업데이트가 손실됨 |
---|---|---|---|---|---|
커밋되지 않은 읽기 | √ | √ | √ | × | √ |
커밋 읽기 | × | √ | √ | × | √ |
반복 읽기 | × | × | √ | × | × |
직렬화 | × | × | × | × | × |
4.Mysql的锁机制:
InnoDB:只有行级别的锁和表级别的锁;
粒度划分:行锁、表锁
用法划分:乐观锁、悲观锁
类型/基本模式:排他锁、共享锁、意向锁、自增锁
算法:间隙锁、记录锁、插入意向锁、临键锁
1.共享锁:行锁
多个事务对于一个数据都可以共享同一把锁,都能访问到数据,只能读取,不能修改。
加锁方式
select * from user lock in share mode;
释放:
commit/rollback
2.排他锁:行锁
又称为写锁,与其他锁不能共存,如果一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的锁,只有获取了排他锁的事务可以对改行数据进行读取和修改。
加锁方式:
自动:delete、update、insert
手动:select * from user where id=1 for update;
3.意向共享锁:表锁
意向锁是由数据引擎自己维护的,用户无法手动加锁。
表示事务正准备给数据加共享锁,也就是说加共享锁之前必须先给表加上意向共享锁。
一个事务想给一张表加表锁成功的前提是:没有其他任何事务锁定该表中的任意一行数据,
如果想给表加锁的话:就要全表扫描数据有没有被加锁,意向锁的出现,我们想给表加上标锁的话直接看这张表有没有意向锁。提高加表锁的效率。
4.意向排他锁:表锁
意向锁是由数据引擎自己维护的,用户无法手动加锁。
表示事务正准备给数据加排他锁,也就是说加排他锁之前必须先给表加上意向排他锁。
5.锁的作用:
6.锁到底锁住了什么?
加行锁:没有索引的时候,锁定任何一行数据都会导致锁表。有索引的时候会锁住当前行
通过锁住索引来实现的。不管是通过二级索引加锁还是通过主键索引加锁,最终锁住的都是主键索引。
一张表中不可能没有索引
聚集索引:索引的逻辑顺序决定了数据存储的物理顺序。
1.primary key 优先使用主键索引
2.unique_key --not null 没有主键找一个非空的唯一字段
3._rowid 都不存在使用rowid
聚集索引-主键索引:完整的数据放到了B+树的叶子节点上
二级索引(辅助索引)-非聚集索引:存放的是主键索引的值,根据索引找到主键索引的值,再去主键索引的B+树上找具体数据
所有的辅助索引都要进行回表,二次查询操作。
7.记录锁、间隙锁、临键锁
N条记录,一定有 N+1个间隙,
临键锁和共享锁不冲突,可重复获取。
间隙锁解决幻读问题。
临键锁左开右闭
隔离级别与锁
普通的select使用mvcc技术读取undolog中的快照数据
可重复读快照建立的时间:第一次读取的时候建立快照
读提交快照建立的时间:当前查询之前建立快照
所以可重复读解决了幻读,而读提交没有解决。
悲观锁:
每次获取到数据的时候,都会担心数据被修改,所以每次获取数据的时候都会进行加锁,确保在自己使用的过程中数据不会被别人修改,使用完成后进行数据解锁
期间对该数据进行读写的其他线程都会进行等待
适合写入操作比较频繁的场景
乐观锁:
每次获取数据的时候,都不会担心数据被修改,所以每次获取数据的时候都不会进行加锁。
但是在更新数据的时候需要判断该数据是否被别人修改过。如果数据被其他线程修改,则不进行数据更新;如果没有被修改,则进行数据更新
适合读取操作比较频繁的场景
5.Mysql的索引:
索引方式
索引 | MyISAM引擎 | InnoDB引擎 | Memory引擎 |
---|---|---|---|
B-Tree索引 | 支持 | 支持 | 支持 |
HASH索引 | 不支持 | 不支持 | 支持 |
R-Tree索引 | 支持 | 不支持 | 不支持 |
Full-text索引 | 支持 | 暂不支持(现在支持) | 不支持 |
1.BTREE:
多路自平衡搜索树:Innodb的存储引擎以page为单位,page是管理磁盘的最小单位,默认16k,一个page中有多个磁盘,如果page中每个数据都有助于定位其它数据的位置,那么将会减少磁盘IO,加快效率,BTREE的出现就解决了这个问题,
2.B+TREE:
对BTREE做了优化,减小了树的深度,减小了检索次数,提升效率,
具体区别不做赘述,可以研究下各种Tree的数据结构,
这个博文讲的BTREE和B+TREE就很详细:传送门
3.HASH :
hash是key—>value形式的数据结构,非常适合做索引,hash索引可以一次定位,不需要像树形索引那样逐层查找,因此具有极高的效率。但是Hash索引也有缺点。
1.Hash 索引仅仅能满足"=",“IN"和”<=>"查询,不能使用范围查询;Hash 索引比较的是进行 Hash 运算之后的 Hash 值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的 Hash 算法处理之后的 Hash 值的大小关系,并不能保证和Hash运算前完全一样。
2.Hash 索引无法被用来避免数据的排序操作;由于 Hash 索引中存放的是经过 Hash 计算之后的 Hash 值,而且Hash值的大小关系并不一定和 Hash 运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算;
3.Hash 索引不能利用部分索引键查询;对于组合索引,Hash 索引在计算 Hash 值的时候是组合索引键合并后再一起计算 Hash 值,而不是单独计算 Hash 值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也无法被利用。
4.Hash 索引在任何时候都不能避免表扫描;Hash 索引是将索引键通过 Hash 运算之后,将 Hash运算结果的 Hash 值和所对应的行指针信息存放于一个 Hash 表中,由于不同索引键存在相同 Hash 值,所以即使取满足某个 Hash 键值的数据的记录条数,也无法从 Hash 索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果。
5.Hash 索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高;对于选择性比较低的索引键,如果创建 Hash 索引,那么将会存在大量记录指针信息存于同一个 Hash 值相关联。这样要定位某一条记录时就会非常麻烦,会浪费多次表数据的访问,而造成整体性能低下
4.FULLTEXT:
全文索引的出现是为了解决WHERE name LIKE “%word%"这类针对文本的模糊查询效率较低的问题。
全文索引,目前只有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不过目前只有 CHAR、VARCHAR ,TEXT 列上可以创建全文索引。
5.RTREE:
RTREE在mysql很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种,相对于BTREE,RTREE的优势在于范围查找
索引类型
-
普通索引:仅加速查询
-
唯一索引:加速查询 + 列值唯一(可以有null)
-
主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个
-
组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
-
全文索引:对文本的内容进行分词,进行搜索
人家菜鸟已经写的很好了,我就不瞎总结了:传送门
6.视图:
视图是一个虚拟表,它可以像表一样进行curd操作,视图是虚拟表,本身不存储数据,而是按照指定的方式进行查询,其本质就是一条有名字的select语句:一到多张表的内容。视图的列来自于一个表或多个表,可以理解为一个复杂查询的sql语句设置了一个别名,所以视图不可以和表名重名,多用作查询,一般不会通过视图去修改数据。
优点:限制数据库的访问,简化查询,数据独立性,对同一数据的不同体现。
创建视图 create view
例如:两个表的关联查询可以创建成一个视图
create view view_test as(select s.id as id,s.name as name,g.age as age from user s,user_detail g where s.id=g.id);
视图运用
使用视图和使用表完全一样
修改视图 CREATE OR REPLACE VIEW
修改视图:
CREATE OR REPLACE VIEW view_test
AS
SELECT * FROM user_detail
WHERE age >= 18;
视图与数据变更
源表的数据变化,数据中的数据也会变化
可以通过视图插入数据,但是只能基于一个基础表进行插入,不能跨表更新数据,不建议在视图更新数据。
如果在创建视图的时候制定了“WITH CHECK OPTION”,那么更新数据时不能插入或更新不符合视图限制条件的记录。
create view view_test as SELECT * FROM user_detail
WHERE age >= 18 WITH CHECK OPTION;
那么如果在视图中插入一条age=12的数据,会提示错误:CHECK OPTION FAILED,因为视图限制了视图大于等于18。
7.Mysql优化:
MySQL 常用30种SQL查询语句优化方法:
1.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
2.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
3.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描。可以在num上设置默认值0,确保表中num列没有null值。
4.尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,可以使用union all。
5.like匹配查询:%放前面会导致索引失效
6.in 和 not in 也要慎用,否则会导致全表扫描,对于连续的数值,能用 between 就不要用 in 了
7.如果在 where 子句中使用参数,也会导致全表扫描。 select id from t where num=@num
可以改为强制查询使用索引,select id from t with(index(索引名)) where num=@num
8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。
9.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
10.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使 用,
并且应尽可能的让字段顺序与索引顺序相一致。
11.很多时候用 exists 代替 in 是一个好的选择:
12.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,
视具体情况而定。一个表的索引数较好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。
13.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
14.避免频繁创建和删除临时表,以减少系统表资源的消耗。
MySQL优化常用方法:
-
选取最适用的字段属性
表中字段的宽度设得尽可能小:char 的上限为 255 字节(固定占用空间),varchar 的上限 65535 字节(实际占用空间),text 的上限为 65535。
尽量把字段设置为 NOT NULL,执行查询的时候,数据库不用去比较 NULL 值。 -
使用联合 (UNION) 来代替手动创建的临时表
把需要使用临时表的两条或更多的 SELECT 查询合并的一个查询中。 -
使用索引
查询语句当中包含有 MAX (), MIN () 和 ORDERBY 这些命令的时候,性能提高更为明显。
索引应建立在那些将用于 JOIN, WHERE 判断和 ORDER BY 排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引。
8.数据库三范式:
1.拆分到最细:
第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。
2.需要确保数据库表中的每一列都和主键相关,
比如订单表应该维护订单相关信息,商品信息应该是单独的商品信息表,
3.确保每列都和主键列直接相关,而不是间接相关
9.explain 执行计划的运用:
查询结果:
id:表示select查询的序列号,表示查询中执行select子句或操作表的顺序。
id的三种情况:
1.id相同:从上到下依次执行
2.id不同:按照id顺序执行
3.id相同不同同时存在:
select_type:
SIMPLE:简单的select查询
PRIMARY:查询中若包含任何复杂的子部分,最外层查询则被标记为PRIMARY
SUBQUERY:在SELECT或WHERE列表中包含了子查询
DERIVED:FROM列表中包含的子查询被标记为DERIVED,MySQL会递归执行这些子查询,把结果放在临时表中
UNION:若第二个SELECT出现在UNION之后,则被标记为UNION
UNION RESULT:从UNION表获取结果的SELECT
table:当前执行的表
type:type所显示的是查询使用了哪种类型,一般来说,得保证查询至少达到range级别,最好能达到ref
从最好到最差依次是:
system > const > eq_ref > ref > range > index > all
system 表只有一行记录
const 表示通过索引一次就找到了,const用于比较primary key 或者unique索引。因为只匹配一行数据,所以很快。如将主键置于where列表中,MySQL就能将该查询转换为一个常量。
例如:select * from user where id =1;
eq_ref 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。
ref 非唯一性索引扫描,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体。
range 只检索给定范围的行,使用一个索引来选择行,key列显示使用了哪个索引,一般就是在你的where语句中出现between、< 、>、in等的查询,
这种范围扫描索引比全表扫描要好,因为它只需要开始于索引的某一点,而结束于另一点,不用扫描全部索引。
index Full Index Scan,Index与All区别为index类型只遍历索引树。这通常比ALL快,因为索引文件通常比数据文件小。
(也就是说虽然all和Index都是读全表,但index是从索引中读取的,而all是从硬盘读取的)
all Full Table Scan 将遍历全表以找到匹配的行
possible_keys: 显示可能应用在这张表中的索引,一个或多个。查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用。
key: 实际使用的索引,如果为NULL,则没有使用索引。(可能原因包括没有建立索引或索引失效)。
key_len: 表示所引出长度,越短越好,该字段表示索引可能的最大长度,并不表示实际长度,key_len是根据表定义得出的,不是根据表内检索得出的。
ref: 显示索引的那一列呗使用了。
rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数,越少越好。
Extra:包含不适合在其他列中显式但十分重要的额外信息
Using filesort:mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作称为“文件排序”。
Using temporary:使用了用临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by和分组查询group by。
Using index:表示相应的select操作中使用了覆盖索引,避免访问了表的数据行,效率不错。如果同时出现using where,表明索引被用来执行索引键值的查找;
如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。
Using where:表明使用了where过滤
Using join buffer: 表明使用了连接缓存,比如说在查询的时候,多表join的次数非常多,那么将配置文件中的缓冲区的join buffer调大一些。
impossible where: where子句的值总是false,不能用来获取任何元组
select tables optimized away:在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,
查询执行计划生成的阶段即完成优化。
distinct:优化distinct操作,在找到第一匹配的元组后即停止找同样值的动作
10.MyISAM与InnoDB 的区别:
1.InnoDB支持事务,MyISAM不支持。
2.InnoDB支持外键,MyISAM不支持,
3.InnoDB是簇聚索引,使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的,MyISAM是非簇聚索引,也是用B+Tree作为索引结构,索引和数据文件是分离的,索引保存的是数据文件的指针。
4.InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量,速度很快(不能加有任何WHERE条件);
5.Innodb不支持全文索引,而MyISAM支持全文索引,在涉及全文索引领域的查询效率上MyISAM速度更快高;PS:5.7以后的InnoDB支持全文索引了.
6.MyISAM表格可以被压缩后进行查询操作.
7.InnoDB支持表、行(默认)级锁,而MyISAM支持表级锁
8.InnoDB表必须有唯一索引(如主键)(用户没有指定的话会自己找/生产一个隐藏列Row_id来充当默认主键),而Myisam可以没有
9.Innodb存储文件有frm、ibd,而Myisam是frm、MYD、MYI
Innodb:frm是表定义文件,ibd是数据文件
Myisam:frm是表定义文件,myd是数据文件,myi是索引文件