首先说下在Mysql众多存储引擎比较重要的两个
InnoDB和MyISAM
在建立一个表的时候可以指定存储引擎
先说他们两个的不同:
InnoDB支持事务,事务很重要。 支持行级锁(基于索引在索引上加锁)。 支持外键(虽然实际当中不会怎么使用外键,我们要减少表之间实际的外键约束)
他的索引文件和数据文件是组织在一起的数据实际上就是按照主键索引顺序来的(主键索引是聚簇索引)其他的索引(辅助索引)是非聚簇索引。MyISAM的索引是非聚簇索引,索引顺序与实际数据顺序不同,索引的叶子结点存数据的地址。
InnoDB有MVCC 解决读写冲突 MyISAM读是比InnoDB快的,但是写操作远远低于InnoDB
MyISAM count(*) 很快因为用了一个变量记录了行数,但InnoDB可以在设计表结构的时候适当用冗余字段,来解决这个问题
一般情况默认用InnoDB(事务真的很重要,我们也要在读与写之间平衡)
---------------------------------------------------------------------------------------------------------------------------------
Mysql事务
事务有ACID(原子、一致、隔离、持久)四个特性
原子
操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态
一致
事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定(事物执行前后应该在我们定义的业务逻辑上前后都符合,并且也遵从着数据库约束,比如唯一约束)
隔离
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
持久
当事务正确完成后,它对于数据的改变是永久性的
---------------------------------------------------------------------------------------------------------------------------------
事务的隔离级别:
数据库隔离级别有四种:
允许读未提交读
一个事务T1可以读到另一个事务T2未提交的数据,
这样可能会出现读写时的脏读,写写的时候的会出现第一类丢失更新
允许读已提交
一个事务T1只能读取另一个事务T2已提交的数据,
这样不会读写的时候不会出现脏读,仍然会出现重复度的问题,且第二类丢失更新不能避免
可重复度
这是数据库默认的隔离级别,这种隔离级别只会出现幻读(幻读指记录行数)
幻读诗可以接受的
串行化
序列化所有事务串行执行什么问题都不会出现,但是并发效率就低了,一般不用序列化
sql规定的各种隔离级别要解决的问题如图
实际上MySQL的“重复读”就可以解决幻读,因为间隙锁
---------------------------------------------------------------------------------------------------------------------------------
解释下隔离级别会的五种问题:
写写冲突情况下:
第一类丢失更新:事务T1回滚把另一个事务T2已经提交的数据覆盖了
第二类丢失更新:事务T1提交把另一个事务T2已经提交的数据覆盖了
读写情况下:
脏读:事务T1在修改数据回退前,这个数据被另一个事务T2读到了,这个数据读的是脏数据,
不可重复读:在一个事务对一个数据读取两次的结果不一致,因为另一个事务在这个期间修改了这个数据
幻读:在一个事务中两次读取的记录数不一致,另一个事务在这个期间增加或者删除了记录行
---------------------------------------------------------------------------------------------------------------------------------
数据库锁:
悲观锁和乐观锁
悲观锁是数据库自己带的锁(数据库已实现)
锁有共享锁和排他锁,共享锁和共享锁不冲突,排他锁于(共享锁和排他锁)都冲突,
也就是说一个数据可以加上多把共享锁,大家都能读,这时候排他锁不能加上去,也就是不能被修改,
排他锁加到数据上以后,其他的锁都不能加了,也就是这个数据只能被他自己修改了。
InnoDB的行锁是基于索引来实现的,不是根据索引条件查询的就不能用到行锁
乐观锁需要自己在应用层实现,一般用CAS加上失败重试
也可以不重试一般不会不重试
在读多写少的情况下建议用CAS
写多读少的情况下别用CAS
因为CAS重试也是有CPU代价的
开发文档的数值是访问冲突小于20%用乐观重试次数不得小于三次
---------------------------------------------------------------------------------------------------------------------------------
MVCC
MVCC(Multi Version Concurrency Control)多版本并发控制。
是一种快照读,读取的不一定是记录最新的版本
他可以在并发下读不加锁,让读写不冲突,提高数据库并发性能(写写冲突还是得用悲观锁)
当前读和快照读
当前读
select~~ lock in share mode(共享锁),
select~~ for update ; update, insert ,delete(排他锁)
这些真正加锁的都是一种当前读。
当前读就是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁
快照读
快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;
快照读的实现是基于MVCC,MVCC避免了读加锁操作,降低了加锁开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本
不加锁的简单的 SELECT 都属于快照读,例如:
SELECT * FROM 表 WHERe id=1
---------------------------------------------------------------------------------------------------------------------------------
MVCC只能在(已提交读,可重复读)两个隔离级别下工作
他的实现方式是:
通过记录里的隐式字段:事务ID、回滚指针,
在通过读视图来分析这个事务能看到的最新数据,
事务ID是增长的,这当作了事务启动时间用来比较的作用
MVCC 在已提交读,可重复读的时候因为用读视图的方式不同,这也是在两个隔离级别下会不会产生重复读的原因
已提交读 :整个事物用的读视图都是第一个读操时候生成的视图
可重复度:事物的每次读操作都会单独用自己的读视图
---------------------------------------------------------------------------------------------------------------------------------
索引
索引用的数据结构: B+树
没有用B树是因为:
1、机械硬盘有IO速度的限制(机械运动磁头), B+树的非叶子结点不存储数据而是当作索引来使用(这样非叶子结点就小了)这样MySQL在一页能够读取更多的节点更快地缩小检索范围 降低IO
2、 B+树的叶子节点有一条DATA双向链表在做范围查询时只需要遍历这条链表,而B树需要返回父节点在 指向他们的另一个子节点
建立索引
在查询条件列(where)、表连接列建立索引(join), 或者说是排序列(order by),
每个表中限制建立索引的个数约为五, 因为在更改记录行的同时所以也要同步的维护
区分度小的列不易建立索引(影响分叉)
索引失效
索引列进行了计算,用了个别函数(像DATE_SUB()这种还会走索引的),类型的隐式转换
或者说 :在联合索引里没有用到第一个索引(MySQL如果我们只是联合索引的条件顺序写反了会给我们优化成索引的顺序)
在联合索引里B+树 是根据从左到右严格有序的顺序组织(索引用不到第一个,或者中间的,后面就会失效断档)like用%开头同理
联合索引, 当遇到范围查询时后面的索引列会忽略,(但是在5.6版本以后索引下推优化了这个问题 )
当查询的数据量小于30% MySQL 会放弃走所以他认为全表扫描效率会更高 (可以在from 表后后面加force index(索引名), 但是不建议这样用,因为索引修改后,sql不维护会出现问题 )
效率问题:
如果需要建立 A、AB、ABC三个索引,那么只用一个ABC就好了,这样的用一个代价小建立三个也没必要,ABC包含了A和AB



