简介:MySQL索引的分类,建立,分析;MySQL 的配置;事务4大特性;Spring 事务的配置与使用;数据库锁的使用
些许命令
1 | ##查询当前进程 |
QPE各指标详细说明
1 | explain select bookId from book where bookId =2014 \G |
- id: 执行序列
- key:列出优化器选择的索引
- rows : mysql优化器的预估值
key_len: 列出了用于SQL语句的连接的键的长度,对确认索引的有效性及多列索引中用到的列的数目很有效
key_leng:4 //int not null key_leng:5 //int null key_leng:30 //char(30) not null key_leng:32 //varchar(30) not null key_leng:92 //varchar(30) null charset=utf8
- table: 单独行的唯一标示,可能是表名,表别名,或者查询临时表标识符等
select_type:列出了各种表示table列应用的使用方式的类型,常见值:
- simple : 不包含子查询及其他负债语句的简单查询
- primary:为更复杂的查询而创建的首要表(也就是最外层的表),这个类型可以在 derived 和 union混合使用时见到
- derived 当一个表不是物理表时
- union
extra
- using where 表示查询使用了where语句来处理结果
- using temporary 使用了内部临时表 (使用了distinct 或者使用了不同的order by 和group by 列)
- using filesort 这就是order by 语句的结果,cpu密集型的过程
- using index 强调只需要使用索引就可以满足需求,不需要访问数据
- using join buffer 强调在获取连接条件时没有使用索引,如果出现这个值,需要更加具体情况改进性能
- impossible where 强调where语句会导致没有符合条件的行
- select tabls optimized away 通过使用索引,优化器可能仅从聚合函数结果中返回一行
尽管explain命令不会执行SQL语句,但是当执行计划确定时,它会执行from语句中的子查询
索引
基本索引类型,包含单列和多列索引
理解索引
索引存在的用途
保证数据完整性
主键(每个表只能有一个主键,不能包含null值,通过主键可获取表中任何特定列,如果定义了auto increment 列则必然是主键的一部分)
唯一性(可有多个唯一性,可包含null,但是null值唯一)优化数据访问性能
- 改进表的连接操作(join)
- 对结果进行排序
- 简化聚合数据操作
索引的类型
如果为主键,默认是有索引的
- 空/normal:一般索引
- Unique :唯一索引
- Full Text :全文索引
索引方式
理解各种索引数据结构理论
- Btree
- hash
- B-tree
- B+tree
- 散列
- 通信 R-tree
- 全文本
各种存储引擎的索引实现方式
- MyISAM 的B-tree
- InnoDB的 B+tree
- InnoDB的 B-tree
- 内存散列索引
- 内存B-tree索引
- InnoDB 内部散列索引
单例索引
1 | alert table <table> |
- 利用索引限制查询读取的行数 (一个全表扫描的查询,在建立索引后,读取行数会明细减少)
- 使用索引连接表(索引能有效提高关联表操作的性能)
- 理解索引的基数(当一个查询中使用不止一个索引的时候,Mysql会试图找到一个最高效的索引,它通过分析每条索引内部数据分布的统计信息来做到这一点。判断谁拥有更高的基数)
- 使用索引进行模式匹配 like ‘Quee%’ (如果经常要以通配符开通来查询, 常用的方法是在数据库中保证需要查询的值的反序值 email like reverse(‘%qq.com’) )
- 结果排序(索引可以用来对查询结果进行排序 , using filesort 表示mysql内部使用了sort_buffe来对结果进行排序)
添加索引对性能造成的影响
- DML影响 (写性能降低)
- DDL影响 (alert 语句执行更慢)
- 磁盘空间
DML 需要提交的 如 insert,update,delete merge 等
DDL 是数据定义语言,如 drop alert create truncate
更优的索引
覆盖索引
覆盖索引得名于它满足了查询中给定表用到的所有的列,你想包含where,order by ,group by ,select 中的列
有很多理由可以说服用户不要使用 select * ,而覆盖索引就是其中之一
局部索引1
2
3
4
5
6
7
8
9
10
11
12// 计算部分索引平均值
SELECT COUNT(DISTINCT t.show_sentence)/COUNT(1) from t_user_show t;
// 找出部分索引最佳长度(得到结果与平均值相近)
SELECT COUNT(DISTINCT LEFT(t.show_sentence,10) )/COUNT(*) as sel10,
COUNT(DISTINCT LEFT(t.show_sentence,20) )/COUNT(*) as sel20,
COUNT(DISTINCT LEFT(t.show_sentence,30) )/COUNT(*) as sel30,
COUNT(DISTINCT LEFT(t.show_sentence,40) )/COUNT(*) as sel40 from t_user_show t;
// 平均值 == 得到结果与平均值相近
//建部分索引语句
ALTER TABLE t_user_show add key (show_sentence(40));
btree索引的常见误区
以 index(a,b,c) 为例,(注意和顺序有关)
- where a=2 可以用到索引
- where a=1 and b=2 可以用到索引
- where a=1 and b=2 and c=3 可以用到索引
- where b=1 / c=1 不能用到索引
- where a=1 and c=1 a可以发挥索引,c不能使用到索引
- where a=1 and b>10 and c=1 a可以发挥索引,b也可以发挥索引,c不能发挥索引
- where a=1 and b like ‘xxx%’ and c=1 a可以发挥索引,b可以发挥索引,c不能发挥索引
MYSQL配置
内存相关的系统变量
全局内存缓冲区
1
2
3
4key_buffer_size : #定义MyISAM索引码缓冲区的大小,通常叫做码缓存
innodb_buffer_pool_size :#定义InnoDB缓冲池的大小
innodb_additionnal_mem_pool_size :#数据字典及内部数据结构缓冲区大小
quey_cache_size : #查询缓存大小全局/会话内存缓冲区
1
2max_heap_table_size : #定义一个memory存储引擎表的最大容量
tmp_table_size : #内存临时表最大容器,与max_heap_table_szie 密切相关会话缓冲区
1
2
3
4join_buffer_size : #定义当索引无法满足连接时,在两个表之间做全表连接操作时,能够使用的内存缓冲区的大小
sort_buffer_size : #定义当索引无法满足排序时,对结果进行排序使用的内存缓冲区的大小
read_buffer_size : #定义连续数据扫描时,能够使用的内存缓冲区大小
read_md_buffer_size : #定义了有序数据扫描时,能够使用的内存缓冲区大小
日志和工具系统变量
1 | show_query_log : #布尔值,确定是否记录执行缓慢的查询 |
查询相关的系统变量
1 | optmizer_switch : #决定优化器中那个高级索引合并功能被启用 |
SQL的生命周期
截取SQL语句
- 全面查询日志
- 慢查询日志
- 二进制日志
- 进程列表 – 通过State的值为Locked判断
- 引擎状态
- mysql 连接器
- 应用程序代码
- infomation_schema
- performance_schema
- SQL 语句统计插件
- Mysql 代理
- TCP/IP
1
sudo tcpdump -l -i eth0 -w -src or dst port 3306 -c 1000 | strings
优化慢查询
- 最慢的SQL,执行频率高的SQL
- 确认SQL语句的当前操作
- 分析SQL语句和辅助信息
- 优化SQL
- 验证SQL优化效果
MYSQL优化小技巧
整合DDL语句
alert 语句是阻塞的,所以可以把多个alert合并到一个
去除重复索引
重复索引主要有两个影响
1. 所有的DML会很忙,因为需要做更多的工作来保证索引的一致性
2. 磁盘占用更大
可以从 索引列表中判断,一般为 多列索引 与单列索引之间的重复
找到没有被使用的或者无效的索引
无用索引一般可以冲QEP 中key_len来辅助判断
改进索引
bigint 和 int
一般 一个主码 被定义为 bigint auto_increment 时,可以变革为 int unsigned auto_increment 原因是 从8字节减少到4字节,可以显著的提高索引的存储空间
datetime 和 timestamp
同样的原因,由8字节变成了4字节
enum 如果是静态的代码值,如性别
gender enum(‘Male,’Female’) not null default ‘Male’
有3个优点
- 隐式检查数据完整性
- 存储空间为1字节,来存储255个状态
- 更可读,索引更紧凑
注: 但实际的开发过程中,枚举类型的查询操作,是需要加’’的,映射成java类,必然是String,操作上不是很方便
。所以通常情况下,使用 tinyint 来代替它,占用1个字节
null 与not null
最好把一列定义 为 not null ,设置成默认值,在查询语言编写的时候尽量少用 where a is null 会引起全表扫描,可使用默认值为0
连表的隐含转换
当你为表连接选择一个索引时,一定要确保这个数据类型是相同的对于 整数类型,要确保 signed 与 unsigned 统一
IP地址
可以将ipv4地址定义为 int unsigned 类型占用4个字节,而定义为varchar(15) 则要占用12个,效果明显
inet_aton() 与 inet_ntoa() 可以方便的在ip与字符串之间转换
MD5
用char(32) 来存储MD5 是一种常见的技巧,适用于定长逻辑(如密码md5存储)
md5() unhex() hex() length() 函数
N+1 问题 合并 为 In
- limint 语句虽然灵活,但是在limit 2000000,20时 会扫描2000020行,然后丢掉2000000行,进行优化时可以A:加上order by,进行按索引操作; B:将查询条件与序大小进行处理,where a>100 limit 20,这种。
简化SQL语句
- 改进列
- 改进连接
- 重写子查询
- 理解试图带来的影响 –> 优化视图查询,必须优化到视图对应的表
缓存选项
- 当普通数据的变化率相对较低时,缓存SQL结果,能为你带来性能提升
但是对写操作大于读操作的系统会造成性能退化 - 可适用应用程序缓存
随记
- mysql 中的utf-8 与utf-8mb4的区别
mysql 在5.5.3之后新增的 utf-8mb4编码,为utf-8的超集,如无特殊存储要求(如 emoji表情),用utf-8就够了
- MySQL 整型存储范围与占用字节
类型 | 存储字节 | 最小值<-->最大值--> |
---|---|---|
tinyint | 1 | -128 ~~ 127(带符号) / 0~~255(unsigned) |
smallint | 2 | -32768 ~~ 32767(带符号) / 0~~65535(unsigned) |
mediumint | 3 | -8388608 ~~ 8388607(带符号) / 0~~16777215(unsigned) |
int | 4 | -2147483648 ~~ 2147483647(带符号) / 0~~4294967295(unsigned) |
bigint | 8 | -9223372036854775808 ~~ 9223372036854775807(带符号) / 0~~18446744073709551615(unsigned) |
FLOAT | 4 | (-3.402823466E+38,-1.175494351E-38),0,(1.175494351E-38,3.402823466351E+38) |
DOUBLE | 8 | - |
在数据库设计的时候,比如tinyint(1) 与tinyint(2) 占用的空间是一样的,都是1个字节,存储数字的范围都是(带符号,-128–127),不一样的点在于,存储的位数比如你用ZEROFILL,显示查询结果时使用该宽度,还有一点在于java类型与数据库类型的默认转换规则是tinyint(1) 会自动转换成boolean,而tinyint(2)会转换为byte
MySQL 字符占用字节
CHAR、VARCHAR、BINARY、VARBINARY、BLOB、TEXT、ENUM和SET
可灵活指定占多少字节- char(n) 固定长度,char(4)不管是存入几个字符,都将占用4个字节
- char类型的字符串检索速度要比varchar类型的快
- varchar可指定n,text不能指定,内部存储varchar是存入的实际字符数+1个字节(n<=255)或2个字节(n>255),text是实际字符数+2个字节
- varchar可直接创建索引,text创建索引要指定前多少个字符
MySQL 日期占用字节
类型 | 存储字节 | 最小值<-->最大值--> |
---|---|---|
DATE | 3 | YYYY-MM-DD |
TIME | 3 | HH:MM:SS |
YEAR | 1 | YYYY |
DATETIME | 8 | YYYY-MM-DD HH:MM:SS |
TIMESTAMP | 4 | YYYYMMDD HHMMSS |
JAVA中整型存储范围与占用字节
- byte的取值范围为-128~127,占用1个字节(-2的7次方到2的7次方-1)
- short的取值范围为-32768~32767,占用2个字节(-2的15次方到2的15次方-1)
- int的取值范围为(-2147483648~2147483647),占用4个字节(-2的31次方到2的31次方-1)
- long的取值范围为(-9223372036854774808~9223372036854774807),占用8个字节(-2的63次方到2的63次方-1)
原码,反码,补码
- 就二进制而言,最高位表示符号,0-为正,1-为负
- 正 原码=反码=补码
- 负 反码(除最高位符号位不变,其他位取反) 补码(反码之后,加入1)
- 为什么会有反码,补码,因为负数的原码直接参与减法会出错
- 二进制 0+0=0 | 0+1=1+0=1 | 1+1=10(向高位进位) | 二进制加法最高位的进位被舍弃
1 | # 计算10-1=10+(-1) |
- byte 1字节 8位 除去符号位 最大值 (0111 1111) = 127 ,最小值
(1111 1111)= -127 ,但是 规定 -0 (1000 0000) = -128 (以便于补码运算) 所以 byte范围为 -128 ~ 127
- Mysql LAST_INSERT_ID()
- 查询和插入所使用的Connection对象必须是同一个才可以
- LAST_INSERT_ID是与table无关的,如果向表a插入数据后,再向表b插入数据,LAST_INSERT_ID返回表b中的Id值
- 假如你使用一条INSERT语句插入多个行, LAST_INSERT_ID() 只返回插入的第一行数据时产生的值。其原因是这使依靠其它服务器复制同样的 INSERT语句变得简单。
事务
事务的4大特性
- 原子性(atomicity)
所有操作属于一个不可分的整体,不会出现一部分改了,另一部分没改
- 一致性(consistency)
操作出现异常时,会回滚,不会出现前后不一致的情况
- 隔离性(isolation)
针对不同事务,相互之间是不可知的(不同的数据库,事务间的隔离机制不相同)
- 持久性(durability)
事务一旦提交,数据修改就是永久的
无事务问题
- 脏读
- 幻读
- 不可重复读
为避免出现这些问题,那么就需要使用事务
隔离机制
- Read uncommitted 读未提交
就是一个事务可以读取另一个未提交事务的数据
- Read committed 读已提交
就是一个事务要等另一个事务提交后才能读取数据。
- Repeatable read 重复读
就是在开始读取数据(事务开启)时,不再允许修改操作
- Serializable 序列化
事务串行化顺序执行
在MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read (可重复读);而在Oracle数据库中,只支持Serializable (串行化)级别和Read committed (读已提交)这两种级别,其中默认的为Read committed级别。
Compare And Set
高并发“查询并修改”的场景,可以用CAS(Compare and Set)的方式解决数据一致性问题。对应到业务,即在set的时候,加上初始条件的比对
其实这也是一种乐观锁的实现
Spring 事务的传播特性
Spring 在同一事务中,查询两次,会返回上一次查询后变更后的值,及这个值为持久化状态的
数据库锁
乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。
乐观锁
相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间戳。
悲观锁
悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度(悲观),因此,在整个数据处理过程中,将数据处于锁定状态。 悲观锁的实现,往往依靠数据库提供的锁机制 (也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)
1 | //0.开始事务 |