概念说明:mysql
原子性 | 原子性是指事务包含的全部操做要么所有成功,要么所有失败回滚 |
一致性 | 一致性是指事务必须使数据库从一个一致性状态变换到另外一个一致性状态,也就是说一个事务执行以前和执行以后都必须处于一致性状态sql 拿转帐来讲,假设用户A和用户B二者的钱加起来一共是5000,那么无论A和B之间如何转帐,转几回帐,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。数据库 |
隔离性 | 隔离性是当多个用户并发访问数据库时,好比操做同一张表时,数据库为每个用户开启的事务,不能被其余事务的操做所干扰,多个并发事务之间要相互隔离。 |
持久性 | 持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即使是在数据库系统遇到故障的状况下也不会丢失提交事务的操做。 |
脏读 | 事务B读取到事务A尚为提交的数据变动 |
不可重复读 | 事务B先后两次读取一条记录之间,该记录被事务A修改并提交,致使事务B读到了不同的结果 |
幻读 | 事务B按条件匹配到n条记录并修改,但因为修改过程当中事务A新插入符合条件的记录,致使事务B更新完成后仍发现有符合条件的数据未被更新。缓存 幻读与不可重复读在理解上差很少,区别是一个是修改数据一个是新增数据安全 |
数据库不一样的隔离级别对应不一样的隔离现象服务器
隔离识别 | 脏读 | 不可重复读 | 幻读 |
未提交读(Read uncommitted)session |
可能 | 可能 | 可能 |
已提交读(Read committed)架构 |
不可能 | 可能 | 可能 |
可重复读(Repeatable read)并发 |
不可能 | 不可能 | 可能 |
可串行化(Serializable)oracle |
不可能 | 不可能 | 不可能 |
特别注意的是:
1.mysql的事务默认是自动提交的,即每条DML语句执行后都会持久化到磁盘
-- 自动提交参数是默认开启的 -- 该参数可配置全局级别,也可配置session级别 mysql> SHOW GLOBAL VARIABLES LIKE '%autocommit%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set -- 全局级别 SET GLOBAL SESSION autocommit = 0; SHOW GLOBAL VARIABLES LIKE '%autocommit%'; -- session级别 SET SESSION autocommit = 0; SHOW VARIABLES LIKE '%autocommit%';
2.DLL语句是不能回滚的
-- 1. BEGIN; ALTER TABLE xxx.xxx ADD COLUMN `name` varchar(50) NULL ; ROLLBACK; -- 没法回滚 -- 2. 值得注意的是: BEGIN; INSERT ...; UPDATE ...; ALTER TABLE xxx.xxx ADD COLUMN `name` varchar(50) NULL ; ROLLBACK;-- 受DLL语句影响,上面的增删改DML语句一样没法回滚
InnoDB实现了如下两种类型的行锁。
如何加锁呢?首先通常的select语句是不会加锁,也不会被其余事务锁阻塞的。但执行insert/update/delete前数据库会自动加锁,具体以下
共享锁 | 排他锁 | |
自动加锁 | insert前 | update或delete |
手动加锁 | SELECT * FROM xxx LOCK IN SHARE MODE; | SELECT * FROM xxx FOR UPDATE; |
下面是这两个锁的兼容矩阵
当前锁模式/是否兼容/请求锁模式 | 排他锁 | 共享锁 |
排他锁(x锁) | × | × |
共享锁(s锁) | × | √ |
解读:
实践:
共享锁例子
事务A | 事务B |
begin; |
|
-- 添加共享锁 SELECT * FROM admin LOCK IN SHARE MODE; |
begin; |
... | -- 这里能够添加共享锁,且能够查询到数据 SELECT * FROM admin LOCK IN SHARE MODE; -- 更新数据会被阻塞 update admin set xx=xxx; |
commit; |
|
|
--等待A提交或回滚后,B才能提交 commit; |
排他锁例子
事务A | 事务B |
begin; |
|
-- 添加排他锁 SELECT * FROM admin for UPDATE; |
begin; |
... | -- 这里加共享或排他锁都会被阻塞。 SELECT * FROM admin for UPDATE; -- 固然了,普通的select语句是能够查到数据的,不会被阻塞 |
commit; |
|
|
--等待A提交或回滚后,B才能提交 commit; |
上面例子中,B事务会被A事务的事务锁阻塞。等待n秒后会抛出异常。这个秒是能够动态配置的
mysql> SHOW GLOBAL VARIABLES LIKE '%innodb_lock_wait_timeout%'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | innodb_lock_wait_timeout | 50 | +--------------------------+-------+ 1 row in set
当两个连接互相等待对方释放的锁,就会形成死锁,如图
如何避免死锁
mysql主要有如下存储引擎,InnoDB会重点介绍。其余都是不经常使用的
MyISAM
-- innodb相关参数 SHOW GLOBAL VARIABLES LIKE '%innodb%'
MYISAM储存文件的结构是堆表,就是说合插入的顺序是同样的,与数据自己的逻辑没有关系。
InnoDB存储文件的结构是索引组织表(聚簇表),就是存放数据的顺序是根据主键对应的逻辑储存的。如图
所以,
1.根据主键查询数据的效率会更好
2.自增的主键的插入效率较好,而随机主键插入的效率较低
3.InnoDB必须指定主键(设计的时候不声明主键,Mysql会自动分配一个字符串类型的主键)。建议自增
在InnoDB中全部读写操做都必须通过缓存,如图
用户读取某数据,并不是直接读取磁盘里面的数据,而是先查询缓冲池里面是否有该数据,没有就去磁盘取,并储存到缓存池中。写操做也同样。而缓存策略是LRU,也就是最近最少使用次数,不被常用的数据会优先替换出缓缓存池
缓存池调优:
-- 查看缓存池容量。假设服务器内存是16G且只开启了一个mysql实例,那将innodb_buffer_pool_size设置成10G 是比较合理的 SHOW GLOBAL VARIABLES LIKE '%innodb_buffer_pool_size%';
刚刚讲到,读写操做是必须通过缓存池的,而写操做其实是对缓存池里面的数据进行修改。
那mysql是怎么样把数据持久化到磁盘呢。
持久化步骤:
1.用户发起修改数据,mysql首先会修改缓存池里面的数据,并实时把该操做记录在redo log文件中
2.而后直接告诉用户数据修改为功了,而后Mysql会慢慢的异步把redo log文件中的操做持久化到磁盘
这样作的好处是把操做记录 记录在redo log文件效率是很高的。
可是这样会有问题就是万一数据还在redo log文件中,还没持久化到磁盘的状况下,mysql宕机。
那数据是否是就丢失了呢,答案是不会。重启mysql的时候,mysql会自动处理的redo log里面未处理的记录
InnoDB持久化相关参数:
SHOW GLOBAL VARIABLES LIKE '%innodb_flush_log_at_trx_commit%'
这个参数有3种值
值 | 说明 | 安全性与性能 |
0 | 每隔1s写入并持久化一第二天志 | 有可能丢失最多1s的事务修改记录 |
1 | 每次commit都持久化日志 | 全部数据都是安全的,一条都不会丢,频繁写操做会对影响新能 |
2 | 每次提交都持久化到系统的内容中,且每1s持久化一第二天志 | 这和0最大区别是,当mysql宕机而服务器主机没有宕机,数据是不会丢的。可是主机宕机,如断电,那效果和设为0是同样的 |