MySQL架构和历史
1.2 并发控制
只要有多个查询需要在同一个时刻修改数据,都会产生并发控制的问题。
1.2.1 读写锁
在处理并发读或写时,可以通过两种锁解决问题,这两种锁通常被称为 共享锁(shared lock) 和 排他锁(exclusive lock), 也叫 读锁(read lock) 和 写锁(write lock)。
1.2.2 锁的力度
所谓锁的策略,就是在锁的开销和数据的安全性之间的寻求平衡,这种平衡也会影响到性能。每种MySQL引擎都实现了自己的锁策略和锁粒度。最重要的两种锁策略:
表锁(table lock)
MySQL最基本的锁策略,也是开销最小的策略。一个用户在对表进行写操作时,需要先获得写锁,这会阻塞其他用户对该表的所有读写操作!只有没有写锁时,其他读取的用户才能获得读锁,读锁之间是不互相阻塞的。
尽管存储引擎可以管理自己的锁,MySQL本身还是会使用各种有效的表锁来实现不同的目的,如服务器会为诸如ALTER TABLE
之类的语句使用表锁,而忽略引擎的锁机制。
行级锁(row lock) 行级锁可以最大程度的支持并发处理,但同时也带来了最大的锁开销问题。在InnoDB和XtraDB实现了行级锁。行级锁只在存储引擎层实现,而在MySQL服务器层没有实现。
1.3 事务
一个运行良好的事务处理系统,必须具有ACID这些标准特征:
原子性(atomicity)
一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作,要么全部提交成功,要么全部回滚失败。对于一个事务来说,不可以执行其中的一部分操作,这就是事务的原子性。一致性(consistency)
数据库总是从一个一致性状态转换到另一个一致性的状态。没有提交的事务所做的修改不会写到数据库中。隔离性(isolation)
一个事务在提交之前,对其他事务是不可见的。持久性(durability)
一旦事务被提交,则其所做的所有修改就会永久保存在数据库中。
1.3.1 隔离级别
READ UNCOMMITTED 未提交读
事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取到未提交的数据,也被称为脏读(Dirty Read)。
READ COMMITTED 提交读
除MySQL以外,大多数数据库系统的默认隔离级别都是READ COMMITED。一个事务从开始直到提交前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫不可重复读(nonrepeatable read),因为两次执行同样的查询,可能会得到不同的结果。
REPEATABLE READ 可重复读
REPEATABLE READ解决了脏读的问题。但是可重复读还是不能解决幻读(Phantom Read)的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,会产生幻行(Phantom Row)。InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC, Multiversion Concurrency Control)解决了幻读的问题。
可重复读是MySQL的默认隔离级别。
SERIALIZABLE 可串行化
SERIALIZABLE是隔离的最高级别。它通过强制事务串行执行,避免前面说的幻读的问题。可串行读会导致大量的超时和锁争用的问题。实际应用中也很少用到这个隔离级别。
隔离级别 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 | 加锁读 |
---|---|---|---|---|
未提交读 | Yes | Yes | Yes | No |
提交读 | No | Yes | Yes | No |
可重复读 | No | No | Yes | No |
可串行读 | No | No | No | Yes |
1.3.2 死锁
死锁是指两个或者多个事务在同一资源上互相占用并请求锁定对方占用的资源,从而导致恶性循环的现象。当多个事务试图以不同的顺序锁定资源时,就可能会产生死锁。多个事务同事锁定同一个资源时也会产生死锁。
1.3.4 MySQL中的事务
MySQL提供了两种事务型的存储引擎,InnoDB和NDB Cluster。
自动提交
MySQL默认采用的就是自动提交(AUTOCOMMIT)模式。
mysql> SHOW VARIABLES LIKE 'AUTOCOMMIT';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.01 sec)
mysql> SET AUTOCOMMIT=1;
Query OK, 0 rows affected (0.00 sec)
2
3
4
5
6
7
8
9
10
SET AUTOCOMMIT=1; # 开启
SET AUTOCOMMIT=0; # 关闭
2
可以通过配置文件设置整个数据库的隔离级别,也可以改变当前会话的隔离级别:
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
Query OK, 0 rows affected (0.00 sec)
2
在事务中混合使用存储引擎
MySQL服务器层不管理事务,事务是由下层的存储引擎实现的。所以在同一事务中使用多种存储引擎是不可靠的。
隐式和显式锁定 隐式锁定:在事务执行过程中,随时都可以执行锁定,锁只有在执行COMMIT或者ROLLBACK的时候才会释放,并且所有的锁是在同一时刻被释放。InnoDB会根据隔离级别在需要的时候自动加锁。
显式锁定:
SELECT ... LOCK IN SHARE MODE;
SELECT ... FOR UPDATE;
2
1.5 MySQL的存储引擎
显示表的相关信息:
mysql> use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> SHOW TABLE STATUS LIKE 'user'\G
*************************** 1. row ***************************
Name: user
Engine: MyISAM
Version: 10
Row_format: Dynamic
Rows: 10
Avg_row_length: 121
Data_length: 1216
Max_data_length: 281474976710655
Index_length: 4096
Data_free: 0
Auto_increment: NULL
Create_time: 2016-11-02 19:18:32
Update_time: 2018-11-29 22:20:49
Check_time: NULL
Collation: utf8_bin
Checksum: NULL
Create_options:
Comment: Users and global privileges
1 row in set (0.00 sec)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1.5.1 InnoDB存储引擎
InnoDB是MySQL的默认事务型引擎,它被设置用来处理大量的短期事务,短期事务大部分情况是正常提交的,很少会被回滚。
InnoDB概览
InnoDB采用MVCC来支持高并发,并且实现了四个标准的隔离级别。通过间隙锁策略防止幻读的出现。
1.5.2 MyISAM存储引擎
提供了大量的特性,包括全文索引、压缩、空间函数等,大那是不支持事务和行级锁,缺陷就是崩溃后无法安全恢复。
对于只读的数据,或者表比较小,可以忍受修复操作,则依然可以继续使用MyISAM。请不要默认使用MyISAM,而应当默认使用InnoDB。
MyISAM会将表存储在两个文件中:数据文件(.MYD)和索引文件(.MYI)。MyISAM对整张表加锁,而不是针对行(读取时会对需要读到的所有表加共享锁,写入时则对表加排他锁。可以通过CHECK TABLE table_name
检查表的错误,如果有错误,可以通过REPAIR TABLE table_name
进行修复(非常慢)。如果MySQL服务器已经关闭,可以通过myisamchk
命令行工具进行检查和修复操作。
对于MyISAM表,即使是BLOB
和TEXT
等长字段,也可以基于前500个字符创建索引。MyISAM基于分词创建索引来支持全文索引。
1.5.3 MySQL内建的其他存储引擎
- Archieve引擎:非事务型引擎,支持
INSERT
和SELECT
操作。 - Blackhole引擎:没有实现任何存储机制,会记录Blackhole表的日志,用于复制数据到备库,或者只是简单地记录到日志。
- CSV引擎:可以将普通的CSV文件作为MySQL的表来处理,但是不支持索引。
- Memory引擎:如果需要快速访问数据,并且这些数据不会被修改,重启重启以后丢失也没有关系,那么使用Memory表示非常有用的。表级锁,并发写入性能较低。每行的长度是固定的。
1.5.6 转换表的引擎
1. ALERT TABLE
mysql> ALERT TABLE mytable ENGIN = InnoDB;
问题:执行时间长。MySQL会按行将数据从原来的表复制到一张新的表中,在复制期间可能会消耗系统所有I/O能力,同时会在原表上加上读锁。
导出与导入
可以使用mysqldump
工具将数据导出到文件,修改文件中CREATE TABLE
语句,同时修改表名。mysqldump默认会自动在CREATE TABLE语句前加上 DROP TABLE语句。
创建与查询
mysql> CREATE TABLE innodb_table LIKE myisam_table;
mysql> ALTER TABLE innodb_table ENGIN=InnoDB;
mysql> INSERT INTO innodb_table SELECT * FROM myisam_table;
2
3
数据量不大时可以很好地工作,数据大时,可以考虑分批处理。 Percona Toolkit提供了一个pt-online-shema-change的工具,可以比较简单、方便的执行上述过程。