1)安装mysql 服务器端
[root@centos64 opt]# rpm -ivh MySQL-server-5.5.48-1.linux2.6.i386.rpm
2)安装mysql 客户端
[root@centos64 opt]# rpm -ivh MySQL-client-5.5.48-1.linux2.6.i386.rpm
注意:若安装时出现
error: Failed dependencies:
libaio.so.1 is needed by MySQL-server-5.5.48-1.linux2.6.i386
libaio.so.1(LIBAIO_0.1) is needed by MySQL-server-5.5.48-1.linux2.6.i386
libaio.so.1(LIBAIO_0.4) is needed by MySQL-server-5.5.48-1.linux2.6.i386
error: Failed dependencies:
libncurses.so.5 is needed by MySQL-client-5.5.48-1.linux2.6.i386
解决方案: 添加对应的依赖
yum -y install libncurses.so.5
yum -y install libaio.so.1
3)查看mysql的用户和mysql组
即为安装成功
4)mysql 服务的启+停
[root@centos64 opt]# service mysql start
[root@centos64 opt]# service mysql stop
5)ROOT密码设置
[root@centos64 opt]# /usr/bin/mysqladmin -u root password 123456
6) 自启动 mysql 服务
[root@centos64 opt]# chkconfig mysql on
7)mysql 安装位置
路径 | 解释 | 备注 |
---|---|---|
/var/lib/mysql | mysql数据库文件的存放路径 | /var/lib/mysql/atguigu.cloud.pid |
/uer/share/mysql/ | 配置文件目录 | mysql.server命令及配置文件 |
/usr/bin | 相关命令目录 | mysqladmin mysqldump等命令 |
/etc/init.d/mysql | 启停相关脚本 |
在 linux 下查看安装目录 ps -ef|grep mysql
8)修改字符集
① 拷贝文件 my-huge.cnf
[root@centos64 opt]# cd /usr/share/mysql/
[root@centos64 mysql]# cp my-huge.cnf /etc/my.cnf
② 重新启动 MySQL 服务
③ 查看字符集
show variables like '%char%'
默认的客户端和服务器端都用了 latin 1 ,所以会乱码
④ 修改字符集
[root@centos64 etc]# vim /etc/my.cnf
修改此三处
⑤ 重启 mysql
主要配置文件:
二进制日志文件log-bin
运用于主从复制
错误日志log-error
默认时关闭的,记录严重的警告和错误信息,每次启动和关闭的详细信息等
查询日志log
默认关闭,记录查询的sql语句,如果开启会减低mysql的整体性能,因为记录日志也是需要消耗能量的
数据文件
① 两系统 :Windows Linux
② frm文件 :存放表结构
③ myd 文件:存放表数据
④ myi文件:存放表索引
如何配置
Windows :my.ini 文件
Linux:/etc/my.cnf 文件
和其他数据库相比,mysql有点与众不同,它的架构可以在多种不同场景中应用并发挥良好的作用,主要体现在存储引擎的架构上,插件式的存储引擎架构将查询处理和其他系统任务以及数据的存储提取相分离,这种架构可以根据业务的需求选择合适的存储引擎
1)用命令查看mysql现在已提供什么引擎
mysql> show engines;
2)看你的mysql当前默认的存储引擎
mysql> show variables like '%storage_engine';
对比项 | MyISAM | InnoDB |
---|---|---|
主外键 | 不支持 | 支持 |
事务 | 不支持 | 支持 |
行表锁 | 表锁,即使操作一条记录也会锁住整个表,不适合高并发的操作 | 行锁,操作时只锁某一行,不对其它行有影响,适合高并发的操作 |
缓存 | 只缓存索引,不缓存真实数据 | 不仅缓存索引还缓存真实数据,对内存要求较高,而且内存大小对性能有决定性的影响 |
表空间 | 小 | 大 |
关注点 | 性能 | 事务 |
默认安装 | Y | Y |
1)手写
SELECT DISTINCT < select_list > FROM < left_table > < join_type > JOIN < right_table > ON < join_condition > WHERE < where_condition > GROUP BY < group_by_list > HAVING < having_condition > ORDER BY < order_by_condition > LIMIT < limit number >
2)机读
1 FROM <left_table> 2 ON <join_condition> 3 <join_type> JOIN <right_table> 4 WHERE <where_condition> 5 GROUP BY <group_by_list> 6 HAVING <having_condition> 7 SELECT 8 DISTINCT <select_list> 9 ORDER BY <order_by_condition> 10 LIMIT <limit_number>
3)总结
CREATE TABLE `tbl_dept`( `id` INT(11) NOT NULL AUTO_INCREMENT, `deptName` VARCHAR(30) DEFAULT NULL, `locAdd` VARCHAR(40) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; CREATE TABLE `tbl_emp`( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(20) DEFAULT NULL, `deptld` INT(11) DEFAULT NULL, PRIMARY KEY(`id`), KEY `fk_dept_id` (`deptld`) #CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptld`) REFERENCES `tbl_dept` (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; INSERT INTO tbl_dept(deptName,locAdd) VALUES('RD',11); INSERT INTO tbl_dept(deptName,locAdd) VALUES('HR',12); INSERT INTO tbl_dept(deptName,locAdd) VALUES('MK',13); INSERT INTO tbl_dept(deptName,locAdd) VALUES('MIS',14); INSERT INTO tbl_dept(deptName,locAdd) VALUES('FD',15); INSERT INTO tbl_emp(NAME,deptld) VALUES('Z3',1); INSERT INTO tbl_emp(NAME,deptld) VALUES('Z4',1); INSERT INTO tbl_emp(NAME,deptld) VALUES('Z5',1); INSERT INTO tbl_emp(NAME,deptld) VALUES('w5',2); INSERT INTO tbl_emp(NAME,deptld) VALUES(`w6`,2); INSERT INTO tbl_emp(NAME,deptld) VALUES('s7',3); INSERT INTO tbl_emp(NAME,deptld) VALUES('s8',4); INSERT INTO tbl_emp(NAME,deptld) VALUES('s9',5);
索引是帮助MySQL高效获取数据的数据结构
简单理解为:排好序的快速查找数据结构
我们平常所说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉的)结构组织的索引。**其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一索引默认的都是使用B+树索引,统称索引。当然,除了B+树这种类型的索引之外,还有哈希索引(hash index)等
目的: 提高查找效率,可以类比字典。
优势:
劣势:
分类:
语法:
CREATE [UNIQUE] INDEX indexName ON myTable(columnname(length));
ALTER mytable ADD [UNIQUE] INDEX [indexName] ON (columnname(length))
DROP INDEX [indexName] ON myTable;
SHOW INDEX FROM tableName;
1) BTree索引
2)Hash索引
3)full-text全文索引
4)R-Tree索引
需要创建索引:
不需要创建索引:
MySQL Query Optimizer
MySQL常见瓶颈
Explain
1)是什么:
2)能干嘛
3)怎么玩
4)各字段解释
id:
select_type:
1)查询的类型,主要是用于区别普通查询、联合查询、子查询等的复杂查询
table:
显示这一行的数据是关于哪些表的
type:
访问类型排序
type显示的是访问类型,是较为重要的一个指标,结果值从最好到最坏依次是:
system>const>eq_ref>ref>fulltext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>All
显示查询使用了何种类型,从最好到最差依此是:
system>const>eq_ref>ref>range>index>All
system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计
const:表示通过索引一次就找到了,const用于比较primary key或则unique索引。因为只匹配一行数据,所以很快。如将主键置于where列表中,MySQL就能将该查询转换为一个常量
eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
ref:非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体
range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引。一般就是在你的where语句中出现了between、<、>、in等的查询。这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束于另一点,不会扫描全部索引
index:Full Index Scan,index与All区别为index类型只遍历索引树。这通常比All快,因为索引文件通常比数据文件小。(也就是说虽然all和index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)
all:Full Table Scan,将遍历全表以找到匹配的行
一般来说,得保证查询至少达到range级别,最好能达到ref。
possible_keys:
显示可能应用在这张表中的索引,一个或多个。查询涉及到的字段上若存在索引,则该索引将被列出。但不一定被查询实际使用
key:
实际使用的索引。如果为NULL,则没有使用索引。查询中若使用了覆盖索引,则该索引仅出现在key列表中,不会出现在possible_keys列表中。(覆盖索引:查询的字段与建立的复合索引的个数一一吻合)
key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好。key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的
ref
显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值。查询中与其它表关联的字段,外键关系建立索引
rows:
根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
Extra:
包含不适合在其他列中显示但十分重要的额外信息。
创建表:
CREATE TABLE IF NOT EXISTS article( id INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, author_id INT(10) UNSIGNED NOT NULL, category_id INT(10) UNSIGNED NOT NULL, views INT(10) UNSIGNED NOT NULL, comments INT(10) UNSIGNED NOT NULL, title VARCHAR(255) NOT NULL, content TEXT NOT NULL ); INSERT INTO article(author_id,category_id,views,comments,title,content) VALUES (1,1,1,1,'1','1'), (2,2,2,2,'2','2'), (1,1,3,3,'3','3');
SELECT * FROM article; +----+-----------+-------------+-------+----------+-------+---------+ | id | author_id | category_id | views | comments | title | content | +----+-----------+-------------+-------+----------+-------+---------+ | 1 | 1 | 1 | 1 | 1 | 1 | 1 | | 2 | 2 | 2 | 2 | 2 | 2 | 2 | | 3 | 1 | 1 | 3 | 3 | 3 | 3 | +----+-----------+-------------+-------+----------+-------+---------+ 3 rows in set (0.00 sec)
查询案例:
两表索引优化分析:主外键
创建表:
CREATE TABLE IF NOT EXISTS class( id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, card INT(10) UNSIGNED NOT NULL, PRIMARY KEY(id) ); CREATE TABLE IF NOT EXISTS book( bookid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, card INT(10) UNSIGNED NOT NULL, PRIMARY KEY(bookid) ); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
查询案例:
实现两表的连接,连接条件是 class.card = book.card
SELECT * FROM class LEFT JOIN book ON class.card = book.card; +----+------+--------+------+ | id | card | bookid | card | +----+------+--------+------+ | 1 | 12 | 6 | 12 | | 2 | 13 | 8 | 13 | | 2 | 13 | 9 | 13 | | 2 | 13 | 12 | 13 | | 3 | 12 | 6 | 12 | | 4 | 17 | 3 | 17 | | 5 | 11 | NULL | NULL | | 6 | 3 | 4 | 3 | | 7 | 1 | 2 | 1 | | 7 | 1 | 11 | 1 | | 7 | 1 | 15 | 1 | | 8 | 16 | 1 | 16 | | 8 | 16 | 18 | 16 | | 9 | 17 | 3 | 17 | | 10 | 16 | 1 | 16 | | 10 | 16 | 18 | 16 | | 11 | 9 | 17 | 9 | | 12 | 17 | 3 | 17 | | 13 | 18 | 7 | 18 | | 14 | 16 | 1 | 16 | | 14 | 16 | 18 | 16 | | 15 | 7 | NULL | NULL | | 16 | 8 | NULL | NULL | | 17 | 19 | NULL | NULL | | 18 | 9 | 17 | 9 | | 19 | 6 | NULL | NULL | | 20 | 5 | NULL | NULL | | 21 | 6 | NULL | NULL | +----+------+--------+------+ 28 rows in set (0.00 sec)
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card; +----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+ | 1 | SIMPLE | class | ALL | NULL | NULL | NULL | NULL | 21 | NULL | | 1 | SIMPLE | book | ALL | NULL | NULL | NULL | NULL | 20 | Using where; Using join buffer (Block Nested Loop) | +----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+ 2 rows in set (0.00 sec)
添加索引:在右表添加索引
ALTER TABLE 'book' ADD INDEX Y ('card');
ALTER TABLE book ADD INDEX Y (card); Query OK, 0 rows affected (0.30 sec) Records: 0 Duplicates: 0 Warnings: 0 SHOW INDEX FROM book; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | book | 0 | PRIMARY | 1 | bookid | A | 20 | NULL | NULL | | BTREE | | | | book | 1 | Y | 1 | card | A | 20 | NULL | NULL | | BTREE | | | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 2 rows in set (0.00 sec)
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card; +----+-------------+-------+------+---------------+------+---------+-----------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+-----------------+------+-------------+ | 1 | SIMPLE | class | ALL | NULL | NULL | NULL | NULL | 21 | NULL | | 1 | SIMPLE | book | ref | Y | Y | 4 | db01.class.card | 1 | Using index | +----+-------------+-------+------+---------------+------+---------+-----------------+------+-------------+ 2 rows in set (0.00 sec)
添加索引:在右表添加索引
DROP INDEX Y ON book;
ALTER TABLE class ADD INDEX X(card);
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card; +----+-------------+-------+-------+---------------+------+---------+------+------+----------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+----------------------------------------------------+ | 1 | SIMPLE | class | index | NULL | X | 4 | NULL | 21 | Using index | | 1 | SIMPLE | book | ALL | NULL | NULL | NULL | NULL | 20 | Using where; Using join buffer (Block Nested Loop) | +----+-------------+-------+-------+---------------+------+---------+------+------+----------------------------------------------------+ 2 rows in set (0.00 sec)
EXPLAIN SELECT * FROM class RIGHT JOIN book ON class.card = book.card; +----+-------------+-------+------+---------------+------+---------+----------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+----------------+------+-------------+ | 1 | SIMPLE | book | ALL | NULL | NULL | NULL | NULL | 20 | NULL | | 1 | SIMPLE | class | ref | X | X | 4 | db01.book.card | 1 | Using index | +----+-------------+-------+------+---------------+------+---------+----------------+------+-------------+ 2 rows in set (0.00 sec)
分析:
这是因为RIGHT JOIN条件用于确定如何从左表搜索行,右边一定都有,所以左边是我们的关键点,一定需要建立索引
class RIGHT JOIN book :book 里面的数据一定存在于结果集中,我们需要拿着 book 表中的数据,去 class 表中搜索,所以索引需要建立在 class 表中
为了不影响之后的测试,删除该表的 idx_article_ccv 索引
DROP INDEX X ON class;
创建表
CREATE TABLE IF NOT EXISTS phone( phoneid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, card INT(10) UNSIGNED NOT NULL, PRIMARY KEY(phoneid) )ENGINE=INNODB; INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
查询案例
SELECT * FROM class LEFT JOIN book ON class.card = book.card LEFT JOIN phone ON book.card = phone.card;
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card LEFT JOIN phone ON book.card = phone.card;
创建索引
ALTER TABLE book ADD INDEX Y (card); ALTER TABLE phone ADD INDEX Z (card);
ALTER TABLE book ADD INDEX Y (card); Query OK, 0 rows affected (0.06 sec) Records: 0 Duplicates: 0 Warnings: 0 SHOW INDEX FROM book; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | book | 0 | PRIMARY | 1 | bookid | A | 20 | NULL | NULL | | BTREE | | | | book | 1 | Y | 1 | card | A | 20 | NULL | NULL | | BTREE | | | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 2 rows in set (0.00 sec) ALTER TABLE phone ADD INDEX Z (card); Query OK, 0 rows affected (0.05 sec) Records: 0 Duplicates: 0 Warnings: 0 SHOW INDEX FROM phone; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | phone | 0 | PRIMARY | 1 | phoneid | A | 20 | NULL | NULL | | BTREE | | | | phone | 1 | Z | 1 | card | A | 20 | NULL | NULL | | BTREE | | | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 2 rows in set (0.00 sec)
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card=book.card LEFT JOIN phone ON book.card = phone.card; +----+-------------+-------+------+---------------+------+---------+-----------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+-----------------+------+-------------+ | 1 | SIMPLE | class | ALL | NULL | NULL | NULL | NULL | 21 | NULL | | 1 | SIMPLE | book | ref | Y | Y | 4 | db01.class.card | 1 | Using index | | 1 | SIMPLE | phone | ref | Z | Z | 4 | db01.book.card | 1 | Using index | +----+-------------+-------+------+---------------+------+---------+-----------------+------+-------------+ 3 rows in set (0.00 sec)
Join 语句优化的结论
将 left join 看作是两层嵌套 for 循环
索引失效(应该避免)
创建表
CREATE TABLE staffs( id INT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(24)NOT NULL DEFAULT'' COMMENT'姓名', `age` INT NOT NULL DEFAULT 0 COMMENT'年龄', `pos` VARCHAR(20) NOT NULL DEFAULT'' COMMENT'职位', `add_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT'入职时间' )CHARSET utf8 COMMENT'员工记录表'; INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('z3',22,'manager',NOW()); INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('July',23,'dev',NOW()); INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('2000',23,'dev',NOW()); ALTER TABLE staffs ADD INDEX index_staffs_nameAgePos(`name`,`age`,`pos`);
select * from staffs; +----+------+-----+---------+---------------------+ | id | name | age | pos | add_time | +----+------+-----+---------+---------------------+ | 1 | z3 | 22 | manager | 2020-08-04 14:42:33 | | 2 | July | 23 | dev | 2020-08-04 14:42:33 | | 3 | 2000 | 23 | dev | 2020-08-04 14:42:33 | +----+------+-----+---------+---------------------+ 3 rows in set (0.00 sec)
SHOW INDEX FROM staffs; +--------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +--------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | staffs | 0 | PRIMARY | 1 | id | A | 3 | NULL | NULL | | BTREE | | | | staffs | 1 | index_staffs_nameAgePos | 1 | name | A | 3 | NULL | NULL | | BTREE | | | | staffs | 1 | index_staffs_nameAgePos | 2 | age | A | 3 | NULL | NULL | | BTREE | | | | staffs | 1 | index_staffs_nameAgePos | 3 | pos | A | 3 | NULL | NULL | | BTREE | | | +--------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 4 rows in set (0.00 sec)
索引失效判断准则
is null
,is not null
也无法使用索引(早期版本不能走索引,后续版本应该优化过,可以走索引)最佳左匹配法则:带头大哥不能死,中间兄弟不能断
1)只有带头大哥 name 时
2)带头大哥 name 带上小弟 age
3)带头大哥 name 带上小弟 age ,小弟 age 带上小小弟 pos
4)带头大哥 name 挂了
5)带头大哥 name 没挂,小弟 age 跑了
在索引列上进行计算,会导致索引失效,进而转向全表扫描
6)不对带头大哥 name 进行任何操作:key = index_staffs_nameAgePos 表明索引生效
7)对带头大哥 name 进行操作:使用 LEFT 函数截取子串
范围之后全失效
8)精确匹配
9)将 age 改为范围匹配
尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少 select *
10)SELECT * 的写法
EXPLAIN SELECT * FROM staffs WHERE name = 'July'AND age > 23 AND pos = 'dev'; +----+-------------+--------+-------+-------------------------+-------------------------+---------+------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+-------------------------+-------------------------+---------+------+------+-----------------------+ | 1 | SIMPLE | staffs | range | index_staffs_nameAgePos | index_staffs_nameAgePos | 78 | NULL | 1 | Using index condition | +----+-------------+--------+-------+-------------------------+-------------------------+---------+------+------+-----------------------+ 1 row in set (0.00 sec)
11)覆盖索引的写法:Extra = Using where; Using index ,Using index 表示使用索引列进行查询,将大大提高查询的效率
EXPLAIN SELECT name, age, pos FROM staffs WHERE name = 'July'AND age = 23 AND pos = 'dev'; +----+-------------+--------+------+-------------------------+-------------------------+---------+-------------------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+-------------------------+-------------------------+---------+-------------------+------+--------------------------+ | 1 | SIMPLE | staffs | ref | index_staffs_nameAgePos | index_staffs_nameAgePos | 140 | const,const,const | 1 | Using where; Using index | +----+-------------+--------+------+-------------------------+-------------------------+---------+-------------------+------+--------------------------+ 1 row in set (0.00 sec)
12)覆盖索引中包含 range 条件:type = ref 并且 Extra = Using where; Using index ,虽然在查询条件中使用了 范围搜索,但是由于我们只需要查找索引列,所以无需进行全表扫描
EXPLAIN SELECT name, age, pos FROM staffs WHERE name = 'July'AND age > 23 AND pos = 'dev'; +----+-------------+--------+------+-------------------------+-------------------------+---------+-------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+-------------------------+-------------------------+---------+-------+------+--------------------------+ | 1 | SIMPLE | staffs | ref | index_staffs_nameAgePos | index_staffs_nameAgePos | 74 | const | 1 | Using where; Using index | +----+-------------+--------+------+-------------------------+-------------------------+---------+-------+------+--------------------------+ 1 row in set (0.00 sec)
mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
13)在使用 != 会 <> 时会导致索引失效:
is null,is not null 也无法使用索引
like % 写最右
SHOW INDEX from staffs; +--------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +--------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | staffs | 0 | PRIMARY | 1 | id | A | 3 | NULL | NULL | | BTREE | | | | staffs | 1 | index_staffs_nameAgePos | 1 | name | A | 3 | NULL | NULL | | BTREE | | | | staffs | 1 | index_staffs_nameAgePos | 2 | age | A | 3 | NULL | NULL | | BTREE | | | | staffs | 1 | index_staffs_nameAgePos | 3 | pos | A | 3 | NULL | NULL | | BTREE | | | +--------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 4 rows in set (0.00 sec)
解决【like ‘%str%’ 】索引失效的问题:覆盖索引
创建表
CREATE TABLE `tbl_user`( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(20) DEFAULT NULL, `age`INT(11) DEFAULT NULL, `email` VARCHAR(20) DEFAULT NULL, PRIMARY KEY(`id`) )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; INSERT INTO tbl_user(`name`,`age`,`email`)VALUES('1aa1',21,'a@163.com'); INSERT INTO tbl_user(`name`,`age`,`email`)VALUES('2bb2',23,'b@163.com'); INSERT INTO tbl_user(`name`,`age`,`email`)VALUES('3cc3',24,'c@163.com'); INSERT INTO tbl_user(`name`,`age`,`email`)VALUES('4dd4',26,'d@163.com');
select * from tbl_user; +----+------+------+-----------+ | id | name | age | email | +----+------+------+-----------+ | 1 | 1aa1 | 21 | a@163.com | | 2 | 2bb2 | 23 | b@163.com | | 3 | 3cc3 | 24 | c@163.com | | 4 | 4dd4 | 26 | d@163.com | +----+------+------+-----------+ 4 rows in set (0.00 sec)
创建索引
CREATE INDEX idx_user_nameAge ON tbl_user(name, age);
CREATE INDEX idx_user_nameAge ON tbl_user(name, age); Query OK, 0 rows affected (0.05 sec) Records: 0 Duplicates: 0 Warnings: 0 SHOW INDEX FROM tbl_user; +----------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +----------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | tbl_user | 0 | PRIMARY | 1 | id | A | 4 | NULL | NULL | | BTREE | | | | tbl_user | 1 | idx_user_nameAge | 1 | name | A | 4 | NULL | NULL | YES | BTREE | | | | tbl_user | 1 | idx_user_nameAge | 2 | age | A | 4 | NULL | NULL | YES | BTREE | | | +----------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 3 rows in set (0.00 sec)
测试覆盖索引
1)如下 SQL 的索引均不会失效:
EXPLAIN SELECT * FROM tbl_user WHERE NAME LIKE '%aa%'; +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tbl_user | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) EXPLAIN SELECT id, name, age, email FROM tbl_user WHERE NAME LIKE '%aa%'; +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tbl_user | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
字符串不加单引号索引失效
SHOW INDEX FROM staffs; +--------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +--------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | staffs | 0 | PRIMARY | 1 | id | A | 3 | NULL | NULL | | BTREE | | | | staffs | 1 | index_staffs_nameAgePos | 1 | name | A | 3 | NULL | NULL | | BTREE | | | | staffs | 1 | index_staffs_nameAgePos | 2 | age | A | 3 | NULL | NULL | | BTREE | | | | staffs | 1 | index_staffs_nameAgePos | 3 | pos | A | 3 | NULL | NULL | | BTREE | | | +--------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 4 rows in set (0.00 sec) explain select * from staffs where name='2000'; +----+-------------+--------+------+-------------------------+-------------------------+---------+-------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+-------------------------+-------------------------+---------+-------+------+-----------------------+ | 1 | SIMPLE | staffs | ref | index_staffs_nameAgePos | index_staffs_nameAgePos | 74 | const | 1 | Using index condition | +----+-------------+--------+------+-------------------------+-------------------------+---------+-------+------+-----------------------+ 1 row in set (0.00 sec)
explain select * from staffs where name=2000; +----+-------------+--------+------+-------------------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+-------------------------+------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | ALL | index_staffs_nameAgePos | NULL | NULL | NULL | 3 | Using where | +----+-------------+--------+------+-------------------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
少用or,用它连接时会索引失效
SHOW INDEX FROM staffs; +--------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +--------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | staffs | 0 | PRIMARY | 1 | id | A | 3 | NULL | NULL | | BTREE | | | | staffs | 1 | index_staffs_nameAgePos | 1 | name | A | 3 | NULL | NULL | | BTREE | | | | staffs | 1 | index_staffs_nameAgePos | 2 | age | A | 3 | NULL | NULL | | BTREE | | | | staffs | 1 | index_staffs_nameAgePos | 3 | pos | A | 3 | NULL | NULL | | BTREE | | | +--------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 4 rows in set (0.00 sec) explain select * from staffs where name='z3' or name = 'July'; +----+-------------+--------+------+-------------------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+-------------------------+------+---------+------+------+-------------+ | 1 | SIMPLE | staffs | ALL | index_staffs_nameAgePos | NULL | NULL | NULL | 3 | Using where | +----+-------------+--------+------+-------------------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
一般性建议
索引优化的总结
全值匹配我最爱, 最左前缀要遵守;
带头大哥不能死, 中间兄弟不能断;
索引列上少计算, 范围之后全失效;
LIKE 百分写最右, 覆盖索引不写 *;
不等空值还有 OR, 索引影响要注意;
VAR 引号不可丢, SQL 优化有诀窍
分析:
总结
1)永远小表驱动大表,类似嵌套循环Nested Loop
① MySQL支持两种方式的排序:
FileSort和Index,Index效率高。FileSort方式效率较低
Using Index,它指MySQL扫描索引本身完成排序。
② ORDER BY满足两种情况,会使用Index方式排序
ORDER BY语句使用索引最左前列
使用Where子句与ORDER BY子句条件列组合满足索引最左前列
双路排序
MySQL4.1之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据。读取行指针和order by列,对他们进行排序,然后扫描已经排好序的列表,按照列表中的值重新从列表中读取对应的数据输出。
从磁盘取排序字段,在buffer进行排序,再从磁盘读取其他字段
取一批数据,要对磁盘进行了两次扫描,众所周知,I\O是很耗时的,所以在mysql4.1之后,出现了第二种改进的算法,就是单路排序
单路排序
从磁盘读取查询需要的所有列,按照order by列在buffer对它们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据。并且把随机IO变成了顺序IO,但是它会使用更多的空间。
结论及引申出的问题
由于单路是后出的,总体而言好过双路
但是用单路有问题
本来想省一次I/O操作,反而导致了大量的I/O操作,反而得不偿失。(原因:数据的总大小超过sort_buffer的容量)
优化策略
增大sort_buffer_size参数的设置
增大max_length_for_sort_data参数的设置
1)说明
2)查看是否开启及如何开启
3)Case
在生产环境中,如果要手工分析日志,查找、分析SQL,显然是个体力活,MySQL提供了日志分析工具mysqldumpslow。
查看mysqldumpslow的帮助信息:
1)往表里插入1000w数据
2)建表
是mysql提供的可以用来分析当前会话中语句执行的资源消耗情况。可以用于SQL的调优的测量
默认情况下,参数处于关闭状态,并保存最近15次的运行结果
步骤:
1)是否支持,看看当前的mysql版本是否支持
show variables like ‘profiling’;
2)开启功能,默认是关闭,使用前需要开启
3)运行SQL
select * from emp group by id%10 limit 150000;
select * from emp group by id%20 order by 5;
4)查看结果
show profiles;
5)诊断SQL
show profile cpu, block io for query [上一步前面的问题SQL数字号码];
6)日常开发需要注意的结论
converting HEAP to MyISAM:查询结果太大,内存都不够用了往磁盘上搬了
Creating tmp table:创建临时表
拷贝数据到临时表
用完再删除
Copying to tmp table on disk:把内存中临时表复制到磁盘,危险!!
locked
1)配置启用
在mysql的my.cnf 种,设置如下
#开启
general_log=1
#记录日志文件的路径
general_log_file=/path/logfile
#输出格式
log_output=FILE
2)编码启用
set global general_log=1;
set global llog_output='TABLE';
此后,你所编写的sql语句,将会记录到mysql库里的general_log表,可以用下面的命令查看
select * from mysql.general_log;
3)永远不要在生产环境开启这个功能
锁是计算机协调多个进程并发访问某一资源的机制。
锁的分类
从对数据操作的类型(读/写)分:
从对数据操作的粒度分:
特点:
案例分析:
create table mylock ( id int not null primary key auto_increment, name varchar(20) default '' ) engine myisam; insert into mylock(name) values('a'); insert into mylock(name) values('b'); insert into mylock(name) values('c'); insert into mylock(name) values('d'); insert into mylock(name) values('e'); select * from mylock;
mysql> select * from mylock; +----+------+ | id | name | +----+------+ | 1 | a | | 2 | b | | 3 | c | | 4 | d | | 5 | e | +----+------+ 5 rows in set (0.00 sec)
手动加锁和释放锁
mysql> show open tables; +--------------------+----------------------------------------------------+--------+-------------+ | Database | Table | In_use | Name_locked | +--------------------+----------------------------------------------------+--------+-------------+ | performance_schema | events_waits_history | 0 | 0 | | performance_schema | events_waits_summary_global_by_event_name | 0 | 0 | | performance_schema | setup_timers | 0 | 0 | | performance_schema | events_waits_history_long | 0 | 0 | | performance_schema | events_statements_summary_by_digest | 0 | 0 | | performance_schema | mutex_instances | 0 | 0 | | performance_schema | events_waits_summary_by_instance | 0 | 0 | | performance_schema | events_stages_history | 0 | 0 | | mysql | db | 0 | 0 | | performance_schema | events_waits_summary_by_host_by_event_name | 0 | 0 | | mysql | user | 0 | 0 | | mysql | columns_priv | 0 | 0 | | performance_schema | events_statements_history_long | 0 | 0 | | performance_schema | performance_timers | 0 | 0 | | performance_schema | file_instances | 0 | 0 | | performance_schema | events_stages_summary_by_user_by_event_name | 0 | 0 | | performance_schema | events_stages_history_long | 0 | 0 | | performance_schema | setup_actors | 0 | 0 | | performance_schema | cond_instances | 0 | 0 | | mysql | proxies_priv | 0 | 0 | | performance_schema | socket_summary_by_instance | 0 | 0 | | performance_schema | events_statements_current | 0 | 0 | | mysql | event | 0 | 0 | | performance_schema | session_connect_attrs | 0 | 0 | | mysql | plugin | 0 | 0 | | performance_schema | threads | 0 | 0 | | mysql | time_zone_transition_type | 0 | 0 | | mysql | time_zone_name | 0 | 0 | | performance_schema | file_summary_by_event_name | 0 | 0 | | performance_schema | events_waits_summary_by_user_by_event_name | 0 | 0 | | performance_schema | socket_summary_by_event_name | 0 | 0 | | performance_schema | users | 0 | 0 | | mysql | servers | 0 | 0 | | performance_schema | events_waits_summary_by_account_by_event_name | 0 | 0 | | db01 | tbl_emp | 0 | 0 | | performance_schema | events_statements_summary_by_host_by_event_name | 0 | 0 | | db01 | tblA | 0 | 0 | | performance_schema | table_io_waits_summary_by_index_usage | 0 | 0 | | performance_schema | events_waits_current | 0 | 0 | | db01 | user | 0 | 0 | | mysql | procs_priv | 0 | 0 | | performance_schema | events_statements_summary_by_thread_by_event_name | 0 | 0 | | db01 | emp | 0 | 0 | | db01 | tbl_user | 0 | 0 | | db01 | test03 | 0 | 0 | | mysql | slow_log | 0 | 0 | | performance_schema | file_summary_by_instance | 0 | 0 | | db01 | article | 0 | 0 | | performance_schema | objects_summary_global_by_type | 0 | 0 | | db01 | phone | 0 | 0 | | performance_schema | events_waits_summary_by_thread_by_event_name | 0 | 0 | | performance_schema | setup_consumers | 0 | 0 | | performance_schema | socket_instances | 0 | 0 | | performance_schema | rwlock_instances | 0 | 0 | | db01 | tbl_dept | 0 | 0 | | performance_schema | events_statements_summary_by_user_by_event_name | 0 | 0 | | db01 | staffs | 0 | 0 | | db01 | class | 0 | 0 | | mysql | general_log | 0 | 0 | | performance_schema | events_stages_summary_global_by_event_name | 0 | 0 | | performance_schema | events_stages_summary_by_account_by_event_name | 0 | 0 | | performance_schema | events_statements_summary_by_account_by_event_name | 0 | 0 | | performance_schema | table_lock_waits_summary_by_table | 0 | 0 | | performance_schema | hosts | 0 | 0 | | performance_schema | setup_objects | 0 | 0 | | performance_schema | events_stages_current | 0 | 0 | | mysql | time_zone | 0 | 0 | | mysql | tables_priv | 0 | 0 | | performance_schema | table_io_waits_summary_by_table | 0 | 0 | | mysql | time_zone_leap_second | 0 | 0 | | db01 | book | 0 | 0 | | performance_schema | session_account_connect_attrs | 0 | 0 | | db01 | mylock | 0 | 0 | | mysql | func | 0 | 0 | | performance_schema | events_statements_summary_global_by_event_name | 0 | 0 | | performance_schema | events_statements_history | 0 | 0 | | performance_schema | accounts | 0 | 0 | | mysql | time_zone_transition | 0 | 0 | | db01 | dept | 0 | 0 | | performance_schema | events_stages_summary_by_host_by_event_name | 0 | 0 | | performance_schema | events_stages_summary_by_thread_by_event_name | 0 | 0 | | mysql | proc | 0 | 0 | | performance_schema | setup_instruments | 0 | 0 | | performance_schema | host_cache | 0 | 0 | +--------------------+----------------------------------------------------+--------+-------------+ 84 rows in set (0.00 sec)
lock table 表名1 read(write), 表名2 read(write), ...;
unlock tables;
读锁示例
mysql> lock table mylock read;
mysql> select * from mylock; +----+------+ | id | name | +----+------+ | 1 | a | | 2 | b | | 3 | c | | 4 | d | | 5 | e | +----+------+ 5 rows in set (0.00 sec)
mysql> select * from book; ERROR 1100 (HY000): Table 'book' was not locked with LOCK TABLES
mysql> select * from mylock; +----+------+ | id | name | +----+------+ | 1 | a | | 2 | b | | 3 | c | | 4 | d | | 5 | e | +----+------+ 5 rows in set (0.00 sec)
mysql> update mylock set name='a2' where id=1; ERROR 1099 (HY000): Table 'mylock' was locked with a READ lock and can't be updated
mysql> update mylock set name='a2' where id=1;
结论:
写锁示例
mysql> lock table mylock write;
mysql> select * from mylock; +----+------+ | id | name | +----+------+ | 1 | a2 | | 2 | b | | 3 | c | | 4 | d | | 5 | e | +----+------+ 5 rows in set (0.00 sec)
mysql> select * from book; ERROR 1100 (HY000): Table 'book' was not locked with LOCK TABLES
mysql> update mylock set name='a2' where id=1; Query OK, 0 rows affected (0.00 sec)
mysql> select * from mylock;
结论:
案例结论
锁类型 | 可否兼容 | 读锁 | 写锁 |
---|---|---|---|
读锁 | 是 | 是 | 否 |
写锁 | 是 | 否 | 否 |
结论:
表锁分析
show open tables;
【如何分析表锁定】可以通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定,通过 show status like 'table%';
命令查看
行锁特点:
事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性。
并发事务处理带来的问题
更新丢失(Lost Update):
1、当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题一一最后的更新覆盖了由其他事务所做的更新。
2、例如,两个程序员修改同一java文件。每程序员独立地更改其副本,然后保存更改后的副本,这样就覆盖了原始文档。最后保存其更改副本的编辑人员覆盖前一个程序员所做的更改
3、如果在一个程序员完成并提交事务之前,另一个程序员不能访问同一文件,则可避免此问题。
脏读(Dirty Reads):
1、一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象地叫做”脏读”
2、一句话:事务A读取到了事务B已修改但尚未提交的的数据,还在这个数据基础上做了操作。此时,如果B事务回滚,A读取的数据无效,不符合一致性要求
不可重复读(Non-Repeatable Reads):
1、一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读”
2、一句话:事务A读取到了事务B已经提交的修改数据,不符合隔离性
幻读(Phantom Reads):
1、一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读一句话:事务A读取到了事务B体提交的新增数据,不符合隔离性
2、幻读和脏读有点类似,脏读是事务B里面修改了数据,幻读是事务B里面新增了数据。
事物的隔离级别
show variables like 'tx_isolation';
mysql 默认是可重复读mysql版本一致且后台以服务运行
主从都配置在[mysqld]结点下,都是小写
主机修改my.ini配置文件
1、(必须)主服务器唯一ID server-id=1
2、(必须)log-bin=自己本地的路径/mysqlbin
3、(可选)log-err=自己本地的路径/mysqlerr
4、(可选)basedir=自己本地路径
5、(可选)temdir=自己本地路径
6、(可选)datadir=自己本地路径/Data/
7、read-only=0
8、【可选】设置不要复制的数据库
binlog-ignore-db=mysql
9、【可选】设置需要复制的数据库
binlog-do-db=需要复制的主数据库名字
从机修改my.cnf配置文件
1、(必须)从服务器唯一ID server-id=2
2、可选】启用二进制日志
因修改过配置文件,请主机+从机都重启后台mysql服务
主机从机都关闭防火墙
service iptables stop
在Windows主机上建立账户并授权slave
1、 GRANT REPLICATION SLAVE ON *.* TO 'zhangsan' @ '192.168.1.100【从机数据库IP】' IDENTIFIED BY '123456';
2、flush privileges;
3、查询master的状态 show master status
记录下File和Position的值
4、执行完此步骤后不要再操作主服务器MYSQL,防止主服务器状态值变化
在Linux从机上配置需要复制的主机
1、CHANGE MASTER TO MASTER_HOST='主机IP', MASTER_USER='zhangsan', MASTER_PASSWORD='123456', MASTER_LOG_FILE='file名字', MASTER_LOG_POS=position数字;
2、启动从服务器复制功能 start slave;
3、show slave status
Slave_IO_Running:Yes
Slave_SQL_Running:Yes
主机新建库、新建表、insert记录,从机复制
停止从服务复制功能 stop slave;