数据库
2025-03-26 22:55:44 0 举报
AI智能生成
数据库
作者其他创作
大纲/内容
数据库安全
数据库安全风险分析及对策
1. 数据安全风险分析
数据被篡改
数据被窃取
用户身份被伪造
2. 典型数据安全攻击方式
Sql注入
网络窃听
未经授权的服务访问
密码破解
3. 数据库管理要点
数据库安全管理员独立性原则
最小权限原则
帐户安全原则
安全审计原则
数据库安全防护原则
数据库基本安全架构
数据库技术
MySQL
MySQL安装
**MySQL在 Windows 下安装**
**MySQL在 Linux 下安装**
**MySQL在 Mac 下安装**
MySQL配置
**添加用户并赋予权限**
**设置字符集**
**设置时区**
数据库操作
**创建数据库**
**删除数据库**
**查看所有数据库**
**使用指定数据库**
表操作
**创建表**
**删除表**
**查看表结构**
**修改表结构(添加列、修改列、删除列)**
数据操作
**插入数据**
**查询数据**
**更新数据**
**删除数据**
查询语句
**where 条件查询**
**in 查询**
**between and 查询**
**like 查询**
**order by 排序**
**limit 限制返回行数**
**distinct 去除重复值**
**聚合函数(count、sum、avg、max、min)**
**group by 分组**
**having 筛选分组**
事务操作
**开启事务**
**提交事务**
**回滚事务**
Redis
基本操作
**字符串**
**列表**
**集合**
**有序集合**
**哈希**
持久化
**RDB 快照**
**AOF 日志**
高级功能
**事务**
**发布与订阅**
**Lua 脚本**
集群
**主从模式**
**哨兵模式**
**Codis 集群**
**Twemproxy 代理**
分布式数据库技术
基本概念
**传统数据库局限**
**分布式数据库优势**
**分布式数据库的难点**
**分布式数据库的分类及架构**
**分布式数据库的解决方案**
**分布式数据库的未来发展方向**
SQL Server for Linux
安装与配置
**在 VirtualBox 中安装 CentOS 7 并部署 SQL Server On Linux**
**在 CentOS 7 中安装 SQL Server On Linux**
**SQL Server On Linux 的启动、停止、重启、配置、卸载**
连接与操作
**SQL Server On Linux 连接**
**SQL Server On Linux 的客户端工具**
**SQL Server On Linux 的备份与恢复**
**SQL Server On Linux 的性能优化**
**从 Windows 迁移 SQL Server On Linux**
**SQL Server On Linux Docker 部署**
**Mac 上 SQL Server On Linux Docker 部署**
**SQL on Linux Docker Volumes**
**SQL on Linux Docker 迁移**
HBase
基本概念
**HBase 原理**
**HBase 的分布式存储架构**
表操作
**HBase 表设计**
**HBase 表操作**
**HBase Shell 命令**
高级功能
**HBase Coprocessor**
**HBase MapReduce 集成**
**HBase 性能调优**
MongoDB
安装与配置
**MongoDB 简介**
**安装与配置 MongoDB**
基本操作
**数据库操作**
**集合操作**
**文档操作**
高级功能
**索引**
**聚合框架**
**复制集**
**分片集群**
**备份与恢复**
**性能调优**
PostgreSQL
基本概念
**系统概述和编译安装**
**系统配置**
**SQL 语法**
**索引**
**约束**
**连接池**
**备份和恢复**
**权限控制**
**PostgreSQL 高可用**
**PostgreSQL 性能测试**
数据层框架
mybatis
快速入门
简介
原生JDBC存在的问题
和hibernate对比
1. Hibernate是全自动的,MyBatis是半自动的 Hibernate实现了部分自动生成SQL
2. MyBatis真正实现了java代码和sql的分离
3. SQL优化上 MyBatis强于Hibernate
4. MyBatis优化维护方便 SQL全存在于XML中 不需要修改源代码
5. 开发效率上 Hibernate略强于mybatiss
6. 从性能上说 因为Mybatis全都是自己手写的SQL,因此性能相对较高反之。Hibernate更加提倡使用HQL,HQL往往会查询更多的字段,从而性能反而较低
工作原理图
分支主题
工作流程描述
主要配置文件
加载配置文件
分支主题
配置数据库信息
分支主题
配置别名
分支主题
加载映射文件
单一加载
分支主题
批量加载
分支主题
映射文件
占位符
#{}表示占位符?解析传递过来的参数
${}对传递过来的参数原样拼接在SQL语句中
主键返回(配置insert语句使用)
mysql 通过SELECT LAST_INSERT_ID()实现
oracle 通过序列 SELECT seq.nextval from dual实现
输入映射(parameterType)
简单类型
pojo类型
包装类型
Map类型
hashmap
输出映射(resultType)
简单类型
pojo类型
resultMap
使用resultMap进行结果映射时,不需要查询的列名和映射的属性名必须一致。但是需要声明一个resultMap,来对列名和属性名进行映射
分支主题
映射关系
一对一()
resultType
使用resultType来进行一对一结果映射,查询出的列的个数和映射的属性的个数要一致。而且映射的属性要存在与一个大的对象中,它是一种平铺式的映射
分支主题
resultMap
使用resultMap来进行一对一结果映射,它是将关联对象添加到主信息的对象中,具体说是对象嵌套对象的一种映射方式
定义Map
分支主题
使用Map
分支主题
一对多()
定义Map
分支主题
使用Map
分支主题
多对多
定义resultMap
分支主题
使用定义的Map
分支主题
逆向工程
会用即可
缓存
一级缓存
介绍
一级缓存指的就是sqlsession,在sqlsession中有一个数据区域,是map结构,这个区域就是一级缓存区域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。一级缓存中的value,就是查询的结果
每个sqlsession中持有了Executor,每个Executor中有一个LocalCache。当用户发起查询时,MyBatis根据当前执行的语句生成MappedStatement,在Local Cache进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入Local Cache,最后返回结果给用户
分支主题
配置
mybatis一级缓存是默认开启的
主配置文件
分支主题
默认是SESSION级别,即在一个MyBatis会话中执行的所有语句,都会共享这一个缓存。另一种一种是STATEMENT级别,可以理解为缓存只对当前执行的这一个Statement有效
工作流程
缓存命中
分支主题
缓存未命中
分支主题
源码剖析
如果是insert/delete/update方法,缓存就会刷新的原因?
分支主题
分支主题
总结
mybatis一级缓存的生命周期和sqlsession一致
mybatis一级缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺
mybatis的一级缓存最大范围是sqlsession内部,有多个sqlsession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement
二级缓存
介绍
二级缓存指的就是同一个namespace下的mapper,二级缓存中,也有一个map结构,这个区域就是一级缓存区域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值
二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个SqlSession共享,是一个全局的变量。当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库
分支主题
配置
主配置文件
分支主题
mybatis的映射xml文件
标签用于声明这个namespace使用二级缓存,并且可以自定义配置
代表引用别的命名空间的Cache配置,两个命名空间的操作使用的是同一个Cache
分支主题
注意
如果不提交事务,二级缓存并不起作用!
总结
mybatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,能够到namespace级别。
mybatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷。
在分布式环境下,由于默认的mybatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将mybatis的Cache接口实现,有一定的开发成本,直接使用Redis,Memcached等分布式缓存成本更低,安全性也更高
缓存的架构图
分支主题
mapper代理
开发规范
mapper接口的全限定名要和mapper映射文件的namespace值一致。
mapper接口的方法名称要和mapper映射文件的statement的id一致。
mapper接口的方法参数类型要和mapper映射文件的statement的parameterType的值一致,而且它的参数是一个。
mapper接口的方法返回值类型要和mapper映射文件的statement的resultType的值一致。
原理
Mapper开发模式底层使用的是jdk的动态代理。
动态SQL
if
可以对输入的参数进行判断
where
默认去掉后面第一个AND 如果没有参数则把自己干掉!
sql片段
让代码有更高的可重用性,先定义后使用,include引入
foreach
循环传递参数
Mysql
硬件
单机
增加内存
更换硬盘
FlashCache
更换CPU
多机
分布式
主从
集群
云存储
优点
硬件优化
软件优化
节省DBA
缺点
数据战略层面安全性
软件
配置
提高并发
调整 max_connections
注意系统的最大进程数和线程数
ulimit -a
调整 TCP 监听请求积压栈大小
back_log
高备用线程缓存
thread_cache_size
引擎
MyISAM
索引缓存优化
设置key_buffer_size值
作用
决定MyISAM 索引块缓存区的大小,它直接影响 MyISAM 表的存取效率
建议配置大小为可用内存大小的 1/4
如: 20G的内存,可以配置 大约 key_buffer_size = 4G
分支主题
默认太小
检测方案
检查索引块写比例 key_writes / key_write_requests
检查索引块读比例 key_reads / key_read_requests
使用多个索引缓存
通过各 session 共享的 key buffer 提高了 MyISAM 索引存储的性能,但它并不能消除 session 间对 key buffer 的竞争
3 调整 “中点插入策略”
默认情况下 MySQL 使用 LRU(Least Recently Used) 策略来选择要淘汰的索引数据块,但这种算法不是很精细,在某些情况下会导致真正的热块被淘汰
中点策略
分成 hot 跟 warm 两个部分,先淘汰 warm 部分的
hot 进入到 warm 才用
读取缓存优化
调整 read_buffer_size 和 read_rnd_buffer_size
注意:
read_buffer_size 是每个 session 独占,如果默认值太大,就会造成内存浪费,甚至导致物理内存耗尽
InnoDB
缓存机制
InnoDB用一块内存区来做 IO 缓存池,不仅用来缓存InnoDB的索引块,而且也用来缓存数据块
参数设置
innodb_buffer_pool_size
该参数决定了 InnoDB 存储引擎表数据和索引数据的最大缓存区大小
值越大,缓存命中率越高,访问 InnoDB 表需要的磁盘 I/O 就越少,性能也就越好
innodb_old_blocks_pct
该参数设置在 LRU list 中,old sublist的比例,设置取值范围是 5 - 95
innodb_old_blocks_time
该参数决定了缓存数据快由 old sublist 转移到 young sublist 的快慢,当一个缓存数据块被插入到 midpoint (old sublist) 后,至少要在 old sublist 停留超过 innodb_old_blocks_time(ms) 才有可能被转移到 new sublist
innodb_buffer_pool_instances
初识Mysql
数据库连接池是干嘛的
mysql自己维护的一个线程池,用来处理连接请求
避免请求频繁创建销毁线程带来的消耗
一条SQL的执行流程
监听SQL请求 -> SQL接口执行SQL -> 解析器解析 -> 选择最优查询路径 -> 执行器执行计划 -> 存储引擎按计划执行SQL -> 访问数据
核心组件
SQL接口
负责处理接收到的SQL
解析器
查询优化器
执行器
存储引擎
MEMORY
Archive
MyISAM
偶尔配合报表系统使用,不过场景也不太多
InnoDB
Buffer Pool
缓存磁盘数据
undo日志文件
记录原数据,提供回滚支持
redo log buffer
对应磁盘上redo log日志文件
记录脏数据,崩溃修复数据支持
binlog日志
InnoDB详解
Buffer Pool缓冲池
组成架构
描述数据块
缓存页
对应磁盘的数据页
一行数据
一行数据
一行数据
...
free链表
维护空闲的缓存页双向链表
本身不占用磁盘空间,实际上是由描述数据里的两个指针组成的
没有空闲缓存页时,触发LRU机制,淘汰不常访问但是被占用着的缓存页
flush链表
脏数据缓存页链表,修改了数据则放入此链表
本身不占用磁盘空间,实际上是由描述数据里的两个指针组成的
后台线程定时将flush链表中的脏数据页都刷入磁盘
LRU链表
用来作为清除不常用的被占用的缓存页的依据
每次数据占用缓存页,就把这个放到头结点
mysql有预读机制,可能导致大量没有用到的缓存页把常用的挤下去了,实际上并没有用到
通过冷热数据分离机制避免此问题
热数据区 - - 冷数据区
默认 63% :37%
即innodb_old_blocks_pct=37
数据加载时会先放入冷数据区,1s后仍有访问,则放入热数据区
即innodb_old_blocks_time=1000
热数据区钱1/4的数据被访问时不会被移动到头结点,以达到LRU链表的性能优化极致
实在没有空闲页时将LRU链表尾节点的缓存页刷到磁盘上,并清空,将数据页加载到这个腾出来的空闲页中
大小设置
默认128M
innodb_buffer_pool_size
每个缓存页都有对应描述数据块,所以Buffer Pool的实际大小可能比我们设置的大5%左右
生产环境优化性能,加大Buffer Pool的大小
一般占机器大小的50~60%
即32GB内存的机器,分配给Buffer Pool 20GB是比较合适的
buffer pool总大小=(chunk大小*buffer pool数量)的倍数,这样是合理
chunk
一系列的描述数据块和缓存页
一个Buffer Pool中多个chunk,共享free、flush、LRU链表
innodb_buffer_pool_chunk_size:默认128M
基于这个机制,Buffer Pool可支持动态扩容
即扩容增加每个Buffer Pool中的chunk数量即可
数据页缓存哈希表
key=表空间号+数据页号;value=缓存页地址
避免缓存页重复加载
undo log日志
事务失败回滚时用
数据格式
undo log
日志开始位置
主键
<列长度,列值>
表id
undo log
日志编号
undo log
日志类型
undo log
日志结束位置
redo log buffer
redo log日志
策略控制参数
innodb_flush_log_at_trx_commit
0--提交事务不刷数据磁盘
宕机就数据就全丢了
1--提交事务从redo log buffer缓存刷入磁盘
2--提交事务从redo log buffer缓存刷入os cache,1s后由os cache刷入磁盘
依旧存在数据丢失风险
用于 重启/崩溃 恢复时按redo log日志恢复之前的数据修改
表空间号+数据页号+偏移量+修改几个字节的值+具体的值
redo log --》 redo log block --》 redo buffer --》 redo log日志文件
应对并发
何时刷入磁盘
redo日志无限写入么
binlog日志
策略
策略控制参数 : sync_binlog
0
数据写入os cache
1
数据写入磁盘
命令
SHOW ENGINE INNODB STATUS
查看当前innodb的具体情况
Transation
涉及问题
脏写
场景
A、B同时发起修改数据动作
原值NULL,A先写入,B随后写入,修改值为B,此时A事务还未提交
然后A发起回滚
直接将值恢复为了NULL
B的修改不见了
脏读
场景
A发起读取操作,B此时发起写入操作
B先写入数据,此时B事务未提交,A读取到B写入的值
此时B发生回滚,A再查发现查不到了
不可重复读
场景
A事务先做了一次读取操作,获取到数据值为x,事务未完成
B发起写入操作将值修改为y
A事务再次读取数据,发现数据值变为了y
幻读
场景
A事务先做了一次读取操作,根据某一条件获取到10条数据,事务未完成
B发起写入操作,写入数据刚好在A的查询范围内
A再次根据条件查询,发现数据变成了11条
SQL标准定义的事务隔离级别
读未提交
read uncommitted
RU
不会发生脏写
读已提交
read committed
RC
不会发生脏读和脏写
可重复读
repeatable read
RR
不会发生脏读、脏写和不可重复读
即依旧可能发生幻读
MySQL中的RR可以避免幻读,默认就是RR
串行化
serializable
不允许事务并发,只能串行执行,性能极差
MySQL的RR如何解决幻读问题
MVCC多版本并发控制机制
前奏 - 什么是undo log版本链
简单来说,每条数据都有两个隐藏字段,
trx_id -> 最近一次更新的事务id,
roll_pointer -> 指向你之前更新这个事务生成的的undo log
示例
事务A
(id=50)
值A
trx_id=50
roll_pointer
事务B
(id=58)
值B
trx_id=58
roll_pointer
分支主题
值A
trx_id=50
roll_pointer
事务C
(id=70)
值C
trx_id=70
roll_pointer
分支主题
值B
trx_id=58
roll_pointer
分支主题
值A
trx_id=50
roll_pointer
基于undo log版本链实现的ReadView机制是什么
简单来说,就是执行一个事务的时候,就生成一个ReadView
m_ids :此时还有哪些事务没有提交
min_trx_id :ids里最小的事务id
max_trx_id :mysql下一个要生成的事务id
creator_trx_id :你这个事务的id
示例
事务A
(id=48)
查询数据X的值
事务B
(id=59)
修改数据X的值
事务C
(id=70)
修改数据X的值
MySQL是如何基于undo log版本链+ReadViem实现避免脏读、不可重复读和幻读的
RC
基于undo log版本链+ReadView多版本控制机制,事务A再每次查询时都会生成一个ReadView视图
根据视图内容及版本链往下查询数据,直到查到符合要求的数据
这里需要注意一点,RC是每次查询都会生成readView视图
即第一次生成视图时,跟你并发发生的数据修改操作,在第二次查询生成视图时已提交
这就导致第二次查询时,已提交的事务大于当前视图min_trx_id,小于max_trx_id,且不在m_ids中
可以读到其他事务提交的值
会有不可重复读和幻读的问题
所以RC是不能避免不可重复读和幻读的
RR
同样是基于undo log版本链+ReadView多版本控制机制
与RC不同的是,RR是在事务开启后的第一次查询生成ReadView视图
即只要RR的事务未执行完,就读不到跟它并发/在它之后执行的修改、新增操作的数据
从而避免了不可重复读和幻读
事务
1、特性
1、原子性
3、隔离性
4、持久性
2、一致性
2、分类
1、扁平事务
2、带有扁平点的扁平事务
3、链事务
4、嵌套事务
5、分布式事务
3、隔离级别
1、read uncommittted
2、read committed
3、repeatable read
4、serializable
innodb 默认解决了 幻读问题
执行过程中中断
innodb: 如果执行 update,delete 过程中及时中断,不会导致事务提交(即不会生效)
myinsm: 由于没有事务的概念,执行到哪里算哪里
mvcc 机制
实现原理
通过保存数据在某个时间点的快照来实现的
通过在每行记录后面保存2个隐藏的列来实现的,一列保存了行的创建时间,一列保存了行的过期时间(或删除时间).但他们都存储的是系统版本号.
对 SQL影响
select
1, InnoDB只查找版本早于当前事务版本的数据行(行的系统版本号小于等于事务的系统版本号)
2, 行的删除号要么未定义,要么大于当前事务版本号,这样可以确保事务读取到的行,在事务开始之前未被删除.
insert
为新插入的每一行保存当前系统版本号做为行版本号
delete
为删除的每一行保存当前系统版本号作为行删除标识
update
插入的每一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识
这样select 在查询时,要保证查询的版本号小于行 删除版本号.
copy on update
索引
索引
索引
什么是索引?
创建一个目录而已!
分类
使用分类
主键索引
不能重复,一个表只能有一个主键。
唯一索引
不能重复,一个表可以有多个唯一索引列
普通索引
可以有重复值
全文索引
第三方工具
联合索引
使用较少
存储分类
BTree 索引
大部分引擎都支持
HASH索引
只有 Memory引擎支持,使用场景简单
全表扫描
RTree索引
是MyISAM引擎的特殊索引
Full-Text索引
建议第三方支持
中文分词
设计原则
给频繁用于查询筛选条件的字段设置为索引
不要过渡索引
查看索引使用情况
SHOW STATUS LIKE 'Handler_read_%';
1、类型
1,Normal:普通的索引;允许一个索引值后面关联多个行值;
2,UNIQUE:唯一索引;允许一个索引值后面只能有一个行值;之前对列添加唯一约束其实就是为这列添加了一个unique索引;当我们为一个表添加一个主键的时候,其实就是为这个表主键列(设置了非空约束),并为主键列添加了一个唯一索引;
3,Fulltext:全文检索,mysql的全文检索只能用myisam引擎,并且性能较低,不建议使用;
2、方法
1,b-tree:是一颗树(二叉树,平衡二叉树,平衡树(B-TREE))
使用平衡树实现索引,是mysql中使用最多的索引类型;在innodb中,存在两种索引类型,第一种是主键索引(primary key),在索引内容中直接保存数据的地址;第二种是其他索引,在索引内容中保存的是指向主键索引的引用;所以在使用innodb的时候,要尽量的使用主键索引,速度非常快;
2,hash:把索引的值做hash运算,并存放到hash表中,使用较少,一般是memory引擎使用;优点:因为使用hash表存储,按照常理,hash的性能比B-TREE效率高很多。
hash索引的缺点:
1,hash索引只能适用于精确的值比较,=,in,或者<>;无法使用范围查询;
2,无法使用索引排序;
3,组合hash索引无法使用部分索引;
4,如果大量索引hash值相同,性能较低;
3、创建
1,较频繁的作为查询条件的字段应该创建索引;
2,唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件;
作为索引的列,如果不能有效的区分数据,那么这个列就不适合作为索引列;比如(性别,状态不多的状态列)
举例:SELECT sum(amount) FROM accountflow WHERE accountType = 0;
假如把accountType作为索引列,因为accountType只有14种,所以,如果根据accountType来创建索引,最多只能按照1/14的比例过滤掉数据;但是,如果可能出现,只按照该条件查询,那我们就要考虑到其他的提升性能的方式了;
3,更新非常频繁的字段不适合创建索引;原因,索引有维护成本;
4,不会出现在WHERE 子句中的字段不该创建索引;
5, 索引不是越多越好;(只为必要的列创建索引)
1,不管你有多少个索引,一次查询至多采用一个索引;(索引和索引之间是独立的)
2,因为索引和索引之间是独立的,所以说每一个索引都应该是单独维护的;数据的增/改/删,会导致所有的索引都要单独维护;
Index 索引
数据存储
数据页间双链表连接
数据页内数据按照id排序,单链表连接
页目录结构, 存储主键 : 数据对应的槽位
数据检索
基于上述数据存储规则
数据检索在数据量不多的情况时进入数据页内做二分查找
数据量多的情况,即一个页一个页查,页内二分查找
全文扫描
页分裂
默认情况下主键id自增,数据页内根据id做单向链,且下一个数据页内的id都比这个页内的大
当你的主键不是自增的,而是自己设置的时候,
就可能会出现一个问题
当前页内有的id比下个页内的还大,或者下个页内的id比当前页小
这个时候就会出现页分裂行为,小的挪到当前页,大的挪到下个页
保证你下一个数据页的主键值都大于上一个数据页的主键值
主键索引
索引结构
每个数据页的页号和最小主键值放在一起,组成索引,存储在目录页中
最小主键值=1 | 页号=2
最小主键值=4| 页号=8
...
先根据最小主键值找到页号,再到数据页里进行二分查找
索引也是分页存储的,分成一个个索引页
索引页树状存储
上层节点保存每个索引页的索引页号和最小值
上层节点存储的索引页数据过多时会再次分裂,上面再加一层
分支主题
索引页56
分支主题
索引页35
分支主题
索引页43
索引页11
索引页20
分支主题
索引页28
B+树
检索时从顶层根据二分查找逐层向下查找
为什么是B+树,不是B树?
B+树子节点相对B树储存的数据更少,能储存的数据更多,树的层级越低
基于二分法(log n)查找,树越低效率越高
叶子节点存储数据形成双向链,方便范围查找
聚簇索引
如果一颗大的B+树数据结构里,叶子节点是它的数据页本身,那么可以称这个B+树索引为聚簇索引
二级索引及回表
针对主键之外字段创建的索引,比如name时
在维护本身主键B+树外,还会根据你这个二级索引创建出来一个B+树,叶子节点也是数据页
但是这个数据页内只存放二级索引字段name 和 主键
组合索引同理
检索时跟主键B+树一样,都是从顶层根据二分查找逐层向下查找
最终获取到的是对应索引内容的数据页
获取到对应的主键id
此时需要根据查到的主键id进行回表操作
即用主键id再去主键B+树查找对应的数据内容
什么是覆盖索引?
基于上述二级索引的创建规则及回表机制
我们在二级索引进行查询时,会先根据二级索引B+树到叶子节点拿到主键id然后在进行回表
回表操作无疑是会很影响效率的
实际上二级索引的叶子节点上是存储了索引字段的数据内容的
那么我们直接取索引对应的内容实际上就可以避免回表操作了,这就是覆盖索引,通过避免回表提升了索引性能
前缀索引
字段特别大的比如varchar(255) 这种,建立索引时可以varchar(20)这种建立
不过这种索引对order by等无效
男女这种字段需要建索引么?为什么?
最好不要对这种字段建立索引,检索到的主键id太多了,再进行回表查询,可能导致比全表扫描还慢
索引是不是越多越好?
根据上述索引规则,每个索引生成一个B+树
增删改需要对每一个索引树进行维护,还有页分裂的情况
索引越多导致的结果就是增删改效率越来越低下,并且占用一定的磁盘空间
索引检索规则及优化
联合索引查询原理及全值匹配原则
查询原理
第一个字段排序->相等的话找第二个字段->然后第三个
下个数据页的三个字段都大于上个数据页
全值匹配原则
where里的几个字段都是根据等值来查询的,即 =
字段名称、顺序也要和联合索引一模一样
最左匹配规则
联合索引不一定要根据字段来查,只要最左边的部分来查询也是可以的
最左前缀匹配规则
比如like
like ‘张%’
这样的查询是可以的
like ‘%张’
这样就不行了,因为它不知道你左面是什么
范围查找规则
where里有范围查询的话,那么只有对联合索引的最左侧查询才能用到索引
比如where class_name > 1 and class_name < 4 and student >
只有class_name是用到索引的
等值匹配+范围匹配规则
比如 where class_name='一班' and student > '' and subject_name < ''
class_name和student是可以用到索引的,subject_name是用不到的
class_name这里用in('一班','二班')
这样也是可以的,这样in相当于=
巧用索引
order by排序时如何运用索引?
先看一个例子
select * from table where xx=xx order by xx1 ,xx2 , xx3 limit 100
即根据某些检索规则查出数据,再排序取前100条
相当于基于磁盘文件来排序 :
filesort
慢到家了
如果我们建立了一个index(xx1,xx2,xx3)的索引,那么还用去 order by 在limit 100么?
不用啊,我们联合索引建立的B+树本身就有排序的呀
因为我们的索引是从小到大这样排序的,那么你想降序的话就要每个字段下都加上desc
order by xx1 desc,xx2 desc, xx3 desc这样才行。
如果你有升有降,那就不行了。
有联合索引之外的字段也要排序或者用了复杂的函数,那也不行了。
所以说我们尽量在SQL中按照联合索引来排序,这样性能就会很高
group by分组时如何运用索引?
大体跟order by差不多
如何设计索引
where、order by、group by尽量能够用到索引
字段特别大的比如varchar(255) 这种,建立索引时可以varchar(20)这种建立
不过这种索引对order by等无效
主键尽量用自增的,uuid的话会经常性发生页分裂
用两三个复杂的联合索引覆盖大部分查询场景,抗下80%的查询请求
再加上几个辅助索引抗下剩余的20非典型请求,基本达到99%的情况下用到索引
例
联合索引
index(province,city,sex,hoppy,dose_log_last_a_month,age)
联合索引最左面都放定值查询的这种字段,将age这种可能到范围查询的放在最后面
中间的一些查询时如果不需要指定查询的话,可以用sex in (1,2)这种,把对应的枚举传递进去
来确保能够使用到索引
辅助索引
index(sex,source)
一般用于where sex = XXX order by source类似场景下
即实际上我们是希望先查到sex = XXX的数据,然后再进行排序
这时就可以利用到索引的原理,先根据最左侧匹配原则获取到sex = XXX的一批数据
然后source在索引上实际上是会给我们按从小到大来排序的,这样直接取前100条即可
避免了sex 和 source建立两个索引反倒无法取舍用到索引的情况
Lock 锁机制
如果说undo log 版本链 + MVCC是对读写并发的处理机制,那么锁机制就是用来应对并发写操作的机制
那么当有人更新数据时,其他事务读取这行数据时需要加锁么?
不需要加锁,undo log版本链 + MVCC解决的就是这个问题,避免频繁加互斥锁
独占锁
多个事务并发更新数据时,都会在行级加上独占锁
独占锁之间都是是互斥的,不会发生脏写
一个事务更新完成后唤醒另一个事务执行更新操作
共享锁
间隙锁
RR时,执行select - > update - > select , 其中update时,需要加间隙锁
即读取当前数据最新值,然后进行更新,需要有加锁操作,这个锁就是间隙锁
??? 还有点问题这个说法
锁
1,、lock
1、共享锁允许事务读取一行数据
2、排他锁允许事务更新或者是删除一条数据
3、意向共享锁事务想要获得一张表某几行的共享锁
4、意向排他锁事务想要获得一张表的某几行的排他锁
2、latch
3、一致性的非锁定读
4、一致性锁定读
5、死锁
explain执行计划
来看看执行计划中的几种专用名词
select_type
这条执行计划对应的什么查询类型
SIMPLE
简单、普通的表查询
PRIMARY
主查询
SUBQUERY
子查询
UNION
取合集
UNION_RESULT
临时表及去重
DERIVED
针对子查询执行的结果集,物化一个临时表
table
查哪个表
partitions
表分区
type
const
性能超高的常量级
比如 select * from table where id = xxx || select * from table where id_no = xxx
即根据聚簇索引 或者 二级索引 + 聚簇索引回源高效定位到数据
需要注意的是二级索引必须是唯一索引
ref
普通的二级索引,非唯一的那种
比如select * from table where name = xxx
即用到了索引,但不是唯一的那种
如果这里是个组合索引的话,那需要按照最左匹配和全值规则来进行查询
eq_ref
被驱动表基于索引做等值匹配
ref_or_null
使用了is null这种的查询
比如select * from table where name is null , 那么无论name是主键还是二级索引,都会是ref_or_null
range
用了索引做范围查询
比如select * from table where age > 10 and age <25
index
针对只遍历二级索引就能拿到你想要的数据,不需要聚簇索引回溯的访问方式,就是index
比如select x1,x2,x3 from table where x2=xxx,其中联合索引为index(x1,x2,x3)
基于遍历二级索引树的叶子节点的方式,相对于索引的二分查找差了些,但是比全表扫描好很多
all
全表扫描,没啥说的
possible_keys
可以使用哪些索引
key
使用的哪个索引
ref
使用某个字段进行等值匹配搜索的时候,跟索引列匹配的那个目标值的一些信息
rows
扫描了多少数据
filtered
经过那个搜索条件后剩余数据的百分比
Extra
Using index
这次查询仅涉及到了二级索引,不需要回表
Using join buffer (Block Nested Loop)
用到了join buffer 提升关联的性能
Using filesort
基于临时磁盘的排序,性能极差
Using temporary
基于临时表的分组group by , 性能极差
SQL优化
先说说基础吧
多表联查
内连接
select * from table1 a,table2 b where a.id = b.f_id
即 inner join
后面没有where 限定条件的话,就会出现笛卡尔积
即 a 驱动表 5条数 , b 被驱动表 10条数据, 最后的结果是 5 * 10 =50
外连接
outer join
select * from table1 a left join table2 b on a.id = b.f_id
即left join
select * from table1 a right join table2 b on a.id = b.f_id
即right join
嵌套循环关联?
上述关联查询分析, 实际上就是取a表所有数据,然后循环每条数据再到b表去查询
如果是三个表的关联查询那么,b表又会拿他的数据去c表里再走一遍查询
一层套一层,即嵌套循环关联
所以我们一般要对关联表字段加上索引
不然如果没有索引,其中一个表走了全表扫描,那就炸了呀。
in适合大表 in 小表
exists适合小表 exists大表
IN 后面跟小表~EXISTS 后面跟大表~~因为 IN 这个单词比 EXISTS 单词更短(更小),EXISTS 这个单词比 IN 更长(更大)
Mysql调优
性能监控
使用show profile查询剖析工具,可以指定具体的type
type
all:显示所有性能信息
show profile all for query n
block io:显示块io操作的次数
show profile block io for query n
context switches:显示上下文切换次数,被动和主动
show profile context switches for query n
cpu:显示用户cpu时间、系统cpu时间
show profile cpu for query n
IPC:显示发送和接受的消息数量
show profile ipc for query n
Memory:暂未实现
page faults:显示页错误数量
show profile page faults for query n
source:显示源码中的函数名称与位置
show profile source for query n
swaps:显示swap的次数
show profile swaps for query n
使用performance schema来更加容易的监控mysql
MYSQL performance schema详解.md
使用show processlist查看连接的线程个数,来观察是否有大量线程处于不正常的状态或者其他不正常的特征
属性说明
id表示session id
user表示操作的用户
host表示操作的主机
db表示操作的数据库
command表示当前状态
sleep:线程正在等待客户端发送新的请求
query:线程正在执行查询或正在将结果发送给客户端
locked:在mysql的服务层,该线程正在等待表锁
analyzing and statistics:线程正在收集存储引擎的统计信息,并生成查询的执行计划
Copying to tmp table:线程正在执行查询,并且将其结果集都复制到一个临时表中
sorting result:线程正在对结果集进行排序
sending data:线程可能在多个状态之间传送数据,或者在生成结果集或者向客户端返回数据
info表示详细的sql语句
time表示相应命令执行时间
state表示命令执行状态
思路
1.定位需要优化的SQL
慢查询日志
打开慢查询日志
pt-query-digest
重点业务流程
压力测试报告
性能监控结果
dwr报告
2.分析SQL低效的原因
Explain
索引利用情况
排序
Explain
临时表
回表读行
行扫描情况
3.修改索引
是否有无用的索引需要删除
是否可以适当建立组合索引
组合索引的顺序是否需要调整
前缀索引
覆盖索引的利用
4.重写SQL
是否因为列的计算导致索引失效
是否有不需要的列别查询: select
是否有不恰当的关联关系导致过多读行
是否可以将复杂的SQL拆分
是否可以指定使用更好的索引: USE INDEX
5.修改表结构
修改表结构以达到更好的索引利用效率
修改列的类型、长度、约束
schema与数据类型优化
数据类型的优化
更小的通常更好
Test.java
简单就好
尽量避免null
实际细则
整数类型
字符和字符串类型
varchar根据实际内容长度保存数据
1、使用最小的符合需求的长度。
2、varchar(n) n小于等于255使用额外一个字节保存长度,n>255使用额外两个字节保存长度。
3、varchar(5)与varchar(255)保存同样的内容,硬盘存储空间相同,但内存空间占用不同,是指定的大小 。
4、varchar在mysql5.6之前变更长度,或者从255一下变更到255以上时时,都会导致锁表。
应用场景
1、存储长度波动较大的数据,如:文章,有的会很短有的会很长
2、字符串很少更新的场景,每次更新后都会重算并使用额外存储空间保存长度
3、适合保存多字节字符,如:汉字,特殊字符等
char固定长度的字符串
1、最大长度:255
2、会自动删除末尾的空格
3、检索效率、写效率 会比varchar高,以空间换时间
应用场景
1、存储长度波动不大的数据,如:md5摘要
2、存储短字符串、经常更新的字符串
BLOB和TEXT类型
datetime和timestamp
datetime
占用8个字节
与时区无关,数据库底层时区配置,对datetime无效
可保存到毫秒
可保存时间范围大
不要使用字符串存储日期类型,占用空间大,损失日期类型函数的便捷性
timestamp
占用4个字节
时间范围:1970-01-01到2038-01-19
精确到秒
采用整形存储
依赖数据库设置的时区
自动更新timestamp列的值
date
占用的字节数比使用字符串、datetime、int存储要少,使用date类型只需要3个字节
使用date类型还可以利用日期时间函数进行日期之间的计算
date类型用于保存1000-01-01到9999-12-31之间的日期
使用枚举代替字符串类型
特殊类型数据
合理使用范式和反范式
范式
优点
范式化的更新通常比反范式要快
当数据较好的范式化后,很少或者没有重复的数据
范式化的数据比较小,可以放在内存中,操作比较快
缺点
通常需要进行关联
反范式
优点
所有的数据都在同一张表中,可以避免关联
可以设计有效的索引;
缺点
表格内的冗余较多,删除数据时候会造成表有些有用的信息丢失
注意
在企业中很好能做到严格意义上的范式或者反范式,一般需要混合使用
在一个网站实例中,这个网站,允许用户发送消息,并且一些用户是付费用户。现在想查看付费用户最近的10条信息。 在user表和message表中都存储用户类型(account_type)而不用完全的反范式化。这避免了完全反范式化的插入和删除问题,因为即使没有消息的时候也绝不会丢失用户的信息。这样也不会把user_message表搞得太大,有利于高效地获取数据。
另一个从父表冗余一些数据到子表的理由是排序的需要。
缓存衍生值也是有用的。如果需要显示每个用户发了多少消息(类似论坛的),可以每次执行一个昂贵的自查询来计算并显示它;也可以在user表中建一个num_messages列,每当用户发新消息时更新这个值。
案例
范式设计
反范式设计
主键的选择
代理主键
与业务无关的,无意义的数字序列
自然主键
事物属性中的自然唯一标识
推荐使用代理主键
它们不与业务耦合,因此更容易维护
一个大多数表,最好是全部表,通用的键策略能够减少需要编写的源码数量,减少系统的总体拥有成本
字符集的选择
1.纯拉丁字符能表示的内容,没必要选择 latin1 之外的其他字符编码,因为这会节省大量的存储空间。
2.如果我们可以确定不需要存放多种语言,就没必要非得使用UTF8或者其他UNICODE字符类型,这回造成大量的存储空间浪费。
3.MySQL的数据类型可以精确到字段,所以当我们需要大型数据库中存放多字节数据的时候,可以通过对不同表不同字段使用不同的数据类型来较大程度减小数据存储量,进而降低 IO 操作次数并提高缓存命中率。
存储引擎的选择
存储引擎的对比
适当的数据冗余
1.被频繁引用且只能通过 Join 2张(或者更多)大表的方式才能得到的独立小字段。
2.这样的场景由于每次Join仅仅只是为了取得某个小字段的值,Join到的记录又大,会造成大量不必要的 IO,完全可以通过空间换取时间的方式来优化。不过,冗余的同时需要确保数据的一致性不会遭到破坏,确保更新的同时冗余字段也被更新。
适当拆分
执行计划
mysql执行计划.md
通过索引进行优化
索引基本知识
索引的优点
1、大大减少了服务器需要扫描的数据量
2、帮助服务器避免排序和临时表
3、将随机io变成顺序io
索引的用处
1、快速查找匹配WHERE子句的行
2、从consideration中消除行,如果可以在多个索引之间进行选择,mysql通常会使用找到最少行的索引
3、如果表具有多列索引,则优化器可以使用索引的任何最左前缀来查找行
4、当有表连接的时候,从其他表检索行数据
5、查找特定索引列的min或max值
6、如果排序或分组时在可用索引的最左前缀上完成的,则对表进行排序和分组
7、在某些情况下,可以优化查询以检索值而无需查询数据行
索引的分类
主键索引
唯一索引
普通索引
全文索引
组合索引
面试技术名词
回表
覆盖索引
最左匹配
索引下推
索引采用的数据结构
哈希表
B+树
索引匹配方式
全值匹配
全值匹配指的是和索引中的所有列进行匹配
explain select * from staffs where name = 'July' and age = '23' and pos = 'dev';
匹配最左前缀
只匹配前面的几列
explain select * from staffs where name = 'July' and age = '23';
explain select * from staffs where name = 'July';
匹配列前缀
可以匹配某一列的值的开头部分
explain select * from staffs where name like 'J%';
explain select * from staffs where name like '%y';
匹配范围值
可以查找某一个范围的数据
explain select * from staffs where name > 'Mary';
精确匹配某一列并范围匹配另外一列
可以查询第一列的全部和第二列的部分
explain select * from staffs where name = 'July' and age > 25;
只访问索引的查询
查询的时候只需要访问索引,不需要访问数据行,本质上就是覆盖索引
explain select name,age,pos from staffs where name = 'July' and age = 25 and pos = 'dev';
哈希索引
基于哈希表的实现,只有精确匹配索引所有列的查询才有效
在mysql中,只有memory的存储引擎显式支持哈希索引
哈希索引自身只需存储对应的hash值,所以索引的结构十分紧凑,这让哈希索引查找的速度非常快
哈希索引的限制
1、哈希索引只包含哈希值和行指针,而不存储字段值,索引不能使用索引中的值来避免读取行
2、哈希索引数据并不是按照索引值顺序存储的,所以无法进行排序
3、哈希索引不支持部分列匹配查找,哈希索引是使用索引列的全部内容来计算哈希值
4、哈希索引支持等值比较查询,也不支持任何范围查询
5、访问哈希索引的数据非常快,除非有很多哈希冲突,当出现哈希冲突的时候,存储引擎必须遍历链表中的所有行指针,逐行进行比较,直到找到所有符合条件的行
6、哈希冲突比较多的话,维护的代价也会很高
案例
组合索引
当包含多个列作为索引,需要注意的是正确的顺序依赖于该索引的查询,同时需要考虑如何更好的满足排序和分组的需要
案例,建立组合索引a,b,c
不同SQL语句使用索引情况
聚簇索引与非聚簇索引
聚簇索引
不是单独的索引类型,而是一种数据存储方式,指的是数据行跟相邻的键值紧凑的存储在一起
优点
1、可以把相关数据保存在一起
2、数据访问更快,因为索引和数据保存在同一个树中
3、使用覆盖索引扫描的查询可以直接使用页节点中的主键值
缺点
1、聚簇数据最大限度地提高了IO密集型应用的性能,如果数据全部在内存,那么聚簇索引就没有什么优势
2、插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式
3、更新聚簇索引列的代价很高,因为会强制将每个被更新的行移动到新的位置
4、基于聚簇索引的表在插入新行,或者主键被更新导致需要移动行的时候,可能面临页分裂的问题
5、聚簇索引可能导致全表扫描变慢,尤其是行比较稀疏,或者由于页分裂导致数据存储不连续的时候
非聚簇索引
数据文件跟索引文件分开存放
覆盖索引
基本介绍
1、如果一个索引包含所有需要查询的字段的值,我们称之为覆盖索引
2、不是所有类型的索引都可以称为覆盖索引,覆盖索引必须要存储索引列的值
3、不同的存储实现覆盖索引的方式不同,不是所有的引擎都支持覆盖索引,memory不支持覆盖索引
优势
1、索引条目通常远小于数据行大小,如果只需要读取索引,那么mysql就会极大的较少数据访问量
2、因为索引是按照列值顺序存储的,所以对于IO密集型的范围查询会比随机从磁盘读取每一行数据的IO要少的多
3、一些存储引擎如MYISAM在内存中只缓存索引,数据则依赖于操作系统来缓存,因此要访问数据需要一次系统调用,这可能会导致严重的性能问题
4、由于INNODB的聚簇索引,覆盖索引对INNODB表特别有用
案例演示
覆盖索引.md
优化小细节
当使用索引列进行查询的时候尽量不要使用表达式,把计算放到业务层而不是数据库层
select actor_id from actor where actor_id=4;
select actor_id from actor where actor_id+1=5;
尽量使用主键查询,而不是其他索引,因此主键查询不会触发回表查询
使用前缀索引
前缀索引实例说明.md
使用索引扫描来排序
使用索引扫描来做排序.md
union all,in,or都能够使用索引,但是推荐使用in
explain select * from actor where actor_id = 1 union all select * from actor where actor_id = 2;
explain select * from actor where actor_id in (1,2);
explain select * from actor where actor_id = 1 or actor_id =2;
范围列可以用到索引
范围条件是:
范围列可以用到索引,但是范围列后面的列无法用到索引,索引最多用于一个范围列
强制类型转换会全表扫描
explain select * from user where phone=13800001234;
不会触发索引
explain select * from user where phone='13800001234';
触发索引
更新十分频繁,数据区分度不高的字段上不宜建立索引
更新会变更B+树,更新频繁的字段建议索引会大大降低数据库性能
类似于性别这类区分不大的属性,建立索引是没有意义的,不能有效的过滤数据,
一般区分度在80%以上的时候就可以建立索引,区分度可以使用 count(distinct(列名))/count(*) 来计算
创建索引的列,不允许为null,可能会得到不符合预期的结果
当需要进行表连接的时候,最好不要超过三张表,因为需要join的字段,数据类型必须一致
能使用limit的时候尽量使用limit
单表索引建议控制在5个以内
单索引字段数不允许超过5个(组合索引)
创建索引的时候应该避免以下错误概念
索引越多越好
过早优化,在不了解系统的情况下进行优化
索引监控
show status like 'Handler_read%';
参数解释
Handler_read_first:读取索引第一个条目的次数
Handler_read_key:通过index获取数据的次数
Handler_read_last:读取索引最后一个条目的次数
Handler_read_next:通过索引读取下一条数据的次数
Handler_read_prev:通过索引读取上一条数据的次数
Handler_read_rnd:从固定位置读取数据的次数
Handler_read_rnd_next:从数据节点读取下一条数据的次数
简单案例
索引优化分析案例.md
查询优化
查询慢的原因
网络
CPU
IO
上下文切换
系统调用
生成统计信息
锁等待时间
优化数据访问
查询性能低下的主要原因是访问的数据太多,某些查询不可避免的需要筛选大量的数据,我们可以通过减少访问数据量的方式进行优化
确认应用程序是否在检索大量超过需要的数据
确认mysql服务器层是否在分析大量超过需要的数据行
是否向数据库请求了不需要的数据
查询不需要的记录
多表关联时返回全部列
总是取出全部列
重复查询相同的数据
执行过程的优化
查询缓存
查询优化处理
语法解析器和预处理
查询优化器
select count(*) from film_actor;
show status like 'last_query_cost';
可以看到这条查询语句大概需要做1104个数据页才能找到对应的数据,这是经过一系列的统计信息计算来的
每个表或者索引的页面个数
索引的基数
索引和数据行的长度
索引的分布情况
在很多情况下mysql会选择错误的执行计划,原因如下:
统计信息不准确
执行计划的成本估算不等同于实际执行的成本
mysql的最优可能跟你想的不一样
mysql不考虑其他并发执行的查询
mysql不会考虑不受其控制的操作成本
优化器的优化策略
静态优化
直接对解析树进行分析,并完成优化
动态优化
动态优化与查询的上下文有关,也可能跟取值、索引对应的行数有关
mysql对查询的静态优化只需要一次,但对动态优化在每次执行时都需要重新评估
优化器的优化类型
重新定义关联表的顺序
将外连接转化成内连接,内连接的效率要高于外连接
使用等价变换规则,mysql可以使用一些等价变化来简化并规划表达式
优化count(),min(),max()
预估并转化为常数表达式,当mysql检测到一个表达式可以转化为常数的时候,就会一直把该表达式作为常数进行处理
索引覆盖扫描,当索引中的列包含所有查询中需要使用的列的时候,可以使用覆盖索引
子查询优化
等值传播
关联查询
join的实现方式原理
Simple Nested-Loop Join
Index Nested-Loop Join
Block Nested-Loop Join
(1)Join Buffer会缓存所有参与查询的列而不是只有Join的列。
(2)可以通过调整join_buffer_size缓存大小
(3)join_buffer_size的默认值是256K,join_buffer_size的最大值在MySQL 5.1.22版本前是4G-1,而之后的版本才能在64位操作系统下申请大于4G的Join Buffer空间。
(4)使用Block Nested-Loop Join算法需要开启优化器管理配置的optimizer_switch的设置block_nested_loop为on,默认为开启。
show variables like '%optimizer_switch%'
案例演示
排序优化
排序的算法
两次传输排序
单次传输排序
当需要排序的列的总大小超过max_length_for_sort_data定义的字节,mysql会选择双次排序,反之使用单次排序,当然,用户可以设置此参数的值来选择排序的方式
优化特定类型的查询
优化count()查询
总有人认为myisam的count函数比较快,这是有前提条件的,只有没有任何where条件的count(*)才是比较快的
使用近似值
更复杂的优化
优化关联查询
确保on或者using子句中的列上有索引,在创建索引的时候就要考虑到关联的顺序
确保任何的groupby和order by中的表达式只涉及到一个表中的列,这样mysql才有可能使用索引来优化这个过程
优化子查询
优化limit分页
优化此类查询的最简单的办法就是尽可能地使用覆盖索引,而不是查询所有的列
select film_id,description from film order by title limit 50,5
explain select film.film_id,film.description from film inner join (select film_id from film order by title limit 50,5) as lim using(film_id);
优化union查询
除非确实需要服务器消除重复的行,否则一定要使用union all,因此没有all关键字,mysql会在查询的时候给临时表加上distinct的关键字,这个操作的代价很高
推荐使用用户自定义变量
自定义变量的使用
set @one :=1
set @min_actor :=(select min(actor_id) from actor)
set @last_week :=current_date-interval 1 week;
自定义变量的限制
1、无法使用查询缓存
2、不能在使用常量或者标识符的地方使用自定义变量,例如表名、列名或者limit子句
3、用户自定义变量的生命周期是在一个连接中有效,所以不能用它们来做连接间的通信
4、不能显式地声明自定义变量地类型
5、mysql优化器在某些场景下可能会将这些变量优化掉,这可能导致代码不按预想地方式运行
6、赋值符号:=的优先级非常低,所以在使用赋值表达式的时候应该明确的使用括号
7、使用未定义变量不会产生任何语法错误
自定义变量的使用案例
优化排名语句
1、在给一个变量赋值的同时使用这个变量
select actor_id,@rownum:=@rownum+1 as rownum from actor limit 10;
2、查询获取演过最多电影的前10名演员,然后根据出演电影次数做一个排名
select actor_id,count(*) as cnt from film_actor group by actor_id order by cnt desc limit 10;
避免重新查询刚刚更新的数据
当需要高效的更新一条记录的时间戳,同时希望查询当前记录中存放的时间戳是什么
update t1 set lastUpdated=now() where id =1;
select lastUpdated from t1 where id =1;
update t1 set lastupdated = now() where id = 1 and @now:=now();
select @now;
确定取值的顺序
在赋值和读取变量的时候可能是在查询的不同阶段
set @rownum:=0;
select actor_id,@rownum:=@rownum+1 as cnt from actor where @rownum<=1;
因为where和select在查询的不同阶段执行,所以看到查询到两条记录,这不符合预期
set @rownum:=0;
select actor_id,@rownum:=@rownum+1 as cnt from actor where @rownum<=1 order by first_name
当引入了orde;r by之后,发现打印出了全部结果,这是因为order by引入了文件排序,而where条件是在文件排序操作之前取值的
解决这个问题的关键在于让变量的赋值和取值发生在执行查询的同一阶段:
set @rownum:=0;
select actor_id,@rownum as cnt from actor where (@rownum:=@rownum+1)<=1;
通过索引进行优化
索引基本知识
索引的优点
1、大大减少了服务器需要扫描的数据量
2、帮助服务器避免排序和临时表
3、将随机io变成顺序io
索引的用处
1、快速查找匹配WHERE子句的行
2、从consideration中消除行,如果可以在多个索引之间进行选择,mysql通常会使用找到最少行的索引
3、如果表具有多列索引,则优化器可以使用索引的任何最左前缀来查找行
4、当有表连接的时候,从其他表检索行数据
5、查找特定索引列的min或max值
6、如果排序或分组时在可用索引的最左前缀上完成的,则对表进行排序和分组
7、在某些情况下,可以优化查询以检索值而无需查询数据行
索引的分类
主键索引
唯一索引
普通索引
全文索引
组合索引
面试技术名词
回表
覆盖索引
最左匹配
索引下推
索引采用的数据结构
哈希表
B+树
索引匹配方式
全值匹配
全值匹配指的是和索引中的所有列进行匹配
explain select * from staffs where name = 'July' and age = '23' and pos = 'dev';
匹配最左前缀
只匹配前面的几列
explain select * from staffs where name = 'July' and age = '23';
explain select * from staffs where name = 'July';
匹配列前缀
可以匹配某一列的值的开头部分
explain select * from staffs where name like 'J%';
explain select * from staffs where name like '%y';
匹配范围值
可以查找某一个范围的数据
explain select * from staffs where name > 'Mary';
精确匹配某一列并范围匹配另外一列
可以查询第一列的全部和第二列的部分
explain select * from staffs where name = 'July' and age > 25;
只访问索引的查询
查询的时候只需要访问索引,不需要访问数据行,本质上就是覆盖索引
explain select name,age,pos from staffs where name = 'July' and age = 25 and pos = 'dev';
哈希索引
基于哈希表的实现,只有精确匹配索引所有列的查询才有效
在mysql中,只有memory的存储引擎显式支持哈希索引
哈希索引自身只需存储对应的hash值,所以索引的结构十分紧凑,这让哈希索引查找的速度非常快
哈希索引的限制
1、哈希索引只包含哈希值和行指针,而不存储字段值,索引不能使用索引中的值来避免读取行
2、哈希索引数据并不是按照索引值顺序存储的,所以无法进行排序
3、哈希索引不支持部分列匹配查找,哈希索引是使用索引列的全部内容来计算哈希值
4、哈希索引支持等值比较查询,也不支持任何范围查询
5、访问哈希索引的数据非常快,除非有很多哈希冲突,当出现哈希冲突的时候,存储引擎必须遍历链表中的所有行指针,逐行进行比较,直到找到所有符合条件的行
6、哈希冲突比较多的话,维护的代价也会很高
案例
组合索引
当包含多个列作为索引,需要注意的是正确的顺序依赖于该索引的查询,同时需要考虑如何更好的满足排序和分组的需要
案例,建立组合索引a,b,c
不同SQL语句使用索引情况
聚簇索引与非聚簇索引
聚簇索引
不是单独的索引类型,而是一种数据存储方式,指的是数据行跟相邻的键值紧凑的存储在一起
优点
1、可以把相关数据保存在一起
2、数据访问更快,因为索引和数据保存在同一个树中
3、使用覆盖索引扫描的查询可以直接使用页节点中的主键值
缺点
1、聚簇数据最大限度地提高了IO密集型应用的性能,如果数据全部在内存,那么聚簇索引就没有什么优势
2、插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式
3、更新聚簇索引列的代价很高,因为会强制将每个被更新的行移动到新的位置
4、基于聚簇索引的表在插入新行,或者主键被更新导致需要移动行的时候,可能面临页分裂的问题
5、聚簇索引可能导致全表扫描变慢,尤其是行比较稀疏,或者由于页分裂导致数据存储不连续的时候
非聚簇索引
数据文件跟索引文件分开存放
覆盖索引
基本介绍
1、如果一个索引包含所有需要查询的字段的值,我们称之为覆盖索引
2、不是所有类型的索引都可以称为覆盖索引,覆盖索引必须要存储索引列的值
3、不同的存储实现覆盖索引的方式不同,不是所有的引擎都支持覆盖索引,memory不支持覆盖索引
优势
1、索引条目通常远小于数据行大小,如果只需要读取索引,那么mysql就会极大的较少数据访问量
2、因为索引是按照列值顺序存储的,所以对于IO密集型的范围查询会比随机从磁盘读取每一行数据的IO要少的多
3、一些存储引擎如MYISAM在内存中只缓存索引,数据则依赖于操作系统来缓存,因此要访问数据需要一次系统调用,这可能会导致严重的性能问题
4、由于INNODB的聚簇索引,覆盖索引对INNODB表特别有用
案例演示
覆盖索引.md
优化小细节
当使用索引列进行查询的时候尽量不要使用表达式,把计算放到业务层而不是数据库层
select actor_id from actor where actor_id=4;
select actor_id from actor where actor_id+1=5;
尽量使用主键查询,而不是其他索引,因此主键查询不会触发回表查询
使用前缀索引
前缀索引实例说明.md
使用索引扫描来排序
使用索引扫描来做排序.md
union all,in,or都能够使用索引,但是推荐使用in
explain select * from actor where actor_id = 1 union all select * from actor where actor_id = 2;
explain select * from actor where actor_id in (1,2);
explain select * from actor where actor_id = 1 or actor_id =2;
范围列可以用到索引
范围条件是:、>=、between
范围列可以用到索引,但是范围列后面的列无法用到索引,索引最多用于一个范围列
强制类型转换会全表扫描
explain select * from user where phone=13800001234;
不会触发索引
explain select * from user where phone='13800001234';
触发索引
更新十分频繁,数据区分度不高的字段上不宜建立索引
更新会变更B+树,更新频繁的字段建议索引会大大降低数据库性能
类似于性别这类区分不大的属性,建立索引是没有意义的,不能有效的过滤数据,
一般区分度在80%以上的时候就可以建立索引,区分度可以使用 count(distinct(列名))/count(*) 来计算
创建索引的列,不允许为null,可能会得到不符合预期的结果
当需要进行表连接的时候,最好不要超过三张表,因为需要join的字段,数据类型必须一致
能使用limit的时候尽量使用limit
单表索引建议控制在5个以内
单索引字段数不允许超过5个(组合索引)
创建索引的时候应该避免以下错误概念
索引越多越好
过早优化,在不了解系统的情况下进行优化
索引监控
show status like 'Handler_read%';
参数解释
Handler_read_first:读取索引第一个条目的次数
Handler_read_key:通过index获取数据的次数
Handler_read_last:读取索引最后一个条目的次数
Handler_read_next:通过索引读取下一条数据的次数
Handler_read_prev:通过索引读取上一条数据的次数
Handler_read_rnd:从固定位置读取数据的次数
Handler_read_rnd_next:从数据节点读取下一条数据的次数
简单案例
索引优化分析案例.md
分区表
分区表的应用场景
表非常大以至于无法全部都放在内存中,或者只在表的最后部分有热点数据,其他均是历史数据
分区表的数据更容易维护
批量删除大量数据可以使用清除整个分区的方式
对一个独立分区进行优化、检查、修复等操作
分区表的数据可以分布在不同的物理设备上,从而高效地利用多个硬件设备
可以使用分区表来避免某些特殊的瓶颈
innodb的单个索引的互斥访问
ext3文件系统的inode锁竞争
可以备份和恢复独立的分区
分区表的限制
一个表最多只能有1024个分区,在5.7版本的时候可以支持8196个分区
在早期的mysql中,分区表达式必须是整数或者是返回整数的表达式,在mysql5.5中,某些场景可以直接使用列来进行分区
如果分区字段中有主键或者唯一索引的列,那么所有主键列和唯一索引列都必须包含进来
分区表无法使用外键约束
分区表的原理
分区表的底层原理.md
分区表的类型
范围分区
根据列值在给定范围内将行分配给分区
范围分区.md
列表分区
类似于按range分区,区别在于list分区是基于列值匹配一个离散值集合中的某个值来进行选择
列分区
mysql从5.5开始支持column分区,可以认为i是range和list的升级版,在5.5之后,可以使用column分区替代range和list,但是column分区只接受普通列不接受表达式
hash分区
基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含myql中有效的、产生非负整数值的任何表达式
key分区
类似于hash分区,区别在于key分区只支持一列或多列,且mysql服务器提供其自身的哈希函数,必须有一列或多列包含整数值
子分区
在分区的基础之上,再进行分区后存储
如何使用分区表
全量扫描数据,不要任何索引
索引数据,并分离热点
在使用分区表的时候需要注意的问题
null值会使分区过滤无效
分区列和索引列不匹配,会导致查询无法进行分区过滤
选择分区的成本可能很高
打开并锁住所有底层表的成本可能很高
维护分区的成本可能很高
服务器参数设置
general
datadir=/var/lib/mysql
数据文件存放的目录
socket=/var/lib/mysql/mysql.sock
mysql.socket表示server和client在同一台服务器,并且使用localhost进行连接,就会使用socket进行连接
pid_file=/var/lib/mysql/mysql.pid
存储mysql的pid
port=3306
mysql服务的端口号
default_storage_engine=InnoDB
mysql存储引擎
skip-grant-tables
当忘记mysql的用户名密码的时候,可以在mysql配置文件中配置该参数,跳过权限表验证,不需要密码即可登录mysql
character
character_set_client
客户端数据的字符集
character_set_connection
mysql处理客户端发来的信息时,会把这些数据转换成连接的字符集格式
character_set_results
mysql发送给客户端的结果集所用的字符集
character_set_database
数据库默认的字符集
character_set_server
mysql server的默认字符集
connection
max_connections
mysql的最大连接数,如果数据库的并发连接请求比较大,应该调高该值
max_user_connections
限制每个用户的连接个数
back_log
mysql能够暂存的连接数量,当mysql的线程在一个很短时间内得到非常多的连接请求时,就会起作用,如果mysql的连接数量达到max_connections时,新的请求会被存储在堆栈中,以等待某一个连接释放资源,如果等待连接的数量超过back_log,则不再接受连接资源
wait_timeout
mysql在关闭一个非交互的连接之前需要等待的时长
interactive_timeout
关闭一个交互连接之前需要等待的秒数
log
log_error
指定错误日志文件名称,用于记录当mysqld启动和停止时,以及服务器在运行中发生任何严重错误时的相关信息
log_bin
指定二进制日志文件名称,用于记录对数据造成更改的所有查询语句
binlog_do_db
指定将更新记录到二进制日志的数据库,其他所有没有显式指定的数据库更新将忽略,不记录在日志中
binlog_ignore_db
指定不将更新记录到二进制日志的数据库
sync_binlog
指定多少次写日志后同步磁盘
general_log
是否开启查询日志记录
general_log_file
指定查询日志文件名,用于记录所有的查询语句
slow_query_log
是否开启慢查询日志记录
slow_query_log_file
指定慢查询日志文件名称,用于记录耗时比较长的查询语句
long_query_time
设置慢查询的时间,超过这个时间的查询语句才会记录日志
log_slow_admin_statements
是否将管理语句写入慢查询日志
cache
key_buffer_size
索引缓存区的大小(只对myisam表起作用)
query cache
query_cache_size
查询缓存的大小,未来版本被删除
show status like '%Qcache%';查看缓存的相关属性
Qcache_free_blocks:缓存中相邻内存块的个数,如果值比较大,那么查询缓存中碎片比较多
Qcache_free_memory:查询缓存中剩余的内存大小
Qcache_hits:表示有多少此命中缓存
Qcache_inserts:表示多少次未命中而插入
Qcache_lowmen_prunes:多少条query因为内存不足而被移除cache
Qcache_queries_in_cache:当前cache中缓存的query数量
Qcache_total_blocks:当前cache中block的数量
query_cache_limit
超出此大小的查询将不被缓存
query_cache_min_res_unit
缓存块最小大小
query_cache_type
缓存类型,决定缓存什么样的查询
0表示禁用
1表示将缓存所有结果,除非sql语句中使用sql_no_cache禁用查询缓存
2表示只缓存select语句中通过sql_cache指定需要缓存的查询
sort_buffer_size
每个需要排序的线程分派该大小的缓冲区
max_allowed_packet=32M
限制server接受的数据包大小
join_buffer_size=2M
表示关联缓存的大小
thread_cache_size
Threads_cached:代表当前此时此刻线程缓存中有多少空闲线程
Threads_connected:代表当前已建立连接的数量
Threads_created:代表最近一次服务启动,已创建现成的数量,如果该值比较大,那么服务器会一直再创建线程
Threads_running:代表当前激活的线程数
INNODB
innodb_buffer_pool_size=
该参数指定大小的内存来缓冲数据和索引,最大可以设置为物理内存的80%
innodb_flush_log_at_trx_commit
主要控制innodb将log buffer中的数据写入日志文件并flush磁盘的时间点,值分别为0,1,2
innodb_thread_concurrency
设置innodb线程的并发数,默认为0表示不受限制,如果要设置建议跟服务器的cpu核心数一致或者是cpu核心数的两倍
innodb_log_buffer_size
此参数确定日志文件所用的内存大小,以M为单位
innodb_log_file_size
此参数确定数据日志文件的大小,以M为单位
innodb_log_files_in_group
以循环方式将日志文件写到多个文件中
read_buffer_size
mysql读入缓冲区大小,对表进行顺序扫描的请求将分配到一个读入缓冲区
read_rnd_buffer_size
mysql随机读的缓冲区大小
innodb_file_per_table
此参数确定为每张表分配一个新的文件
mysql集群(后续更新)
主从复制
读写分离
分库分表
语句优化
避免使用
select *
distinct
in / not in
order by
尽量减少额外的排序,通过索引直接返回有效数据
group by
建议不加上排序
条件
OR 是分别查询后 做的联合查询
sql优化原则
1、选择需要优化的SQL
2、Explain和Profile入手
1、任何SQL的优化,都从Explain语句开始;Explain语句能够得到数据库执行该SQL选择的执行计划;
2、首先明确需要的执行计划,再使用Explain检查;
3、使用profile明确SQL的问题和优化的结果;
3、永远用小结果集驱动大的结果集
4、在索引中完成排序
5、使用最小Columns
6、使用最有效的过滤条件
7、避免复杂的JOIN和子查询
MySQL优化
我们可以且应该优化什么?
硬件
操作系统/软件库
SQL服务器(设置和查询)
应用编程接口(API)
应用程序
优化硬件
如果你需要庞大的数据库表(>2G),你应该考虑使用 64 位的硬件结构,像 Apha、Sparc 或即将推出的IA64。因为 MySQL 内部使用大量 64位的整数,64位的 CPU 将提供更好的性能。对大数据库,优化的次序一般是 RAM、快速硬盘、CPU 能力。更多的内存通过将最常用的键码页面存放在内存中可以加速键码的更新。
如果不使用事务安全(transaction-safe)的表或有大表并且想避免长文件检查,一台 UPS 就能够在电原故障时让系统安全关闭。
对于数据库存放在一个专用服务器的系统,应该考虑 1G 的以太网。延迟与吞吐量同样重要。
优化磁盘
为系统、程序和临时文件配备一个专用磁盘,如果确是进行很多修改工作,将更新日志和事务日志放在专用磁盘上。
低寻道时间对数据库磁盘非常重要。对与大表,你可以估计你将需要:
log(行数)/log(索引块长度/3”2/(键码长度 + 数据指针长度))+1次寻到才能找到一行。
对于有 500000 行的表,索引 Mediun int类型的列,需要:log(500000)/log(1024/3*2/(3 + 2)+1=4次寻道。上述索引需要 500000*7*3/2=5.2M的空间。实际上,大多数块将被缓存,所以大概只需要 1-2次寻道。
然而对于写入《如上),你将需要 4次寻道请求来找到在哪里存放新键码,而且一般要 2次寻道来更新索引并写入一行。
对于非常大的数据库,你的应用将受到磁盘寻道速度的限制,随着数据里的增加呈 N og N 数据级递
将数据库和表分在不同的磁盘上。在 MySOL中,你可以为此而使用符号链接。条列磁盘(RAID 0)将提高读和写的吞吐量。带像的条列(RAID 0+1)将更安全并提高读取的吞吐量与入的吞吐量将有所降低。不要对临时文件或可以很容易地重建的数据所在的磁盘使用像或 RAID(除了RAID 0)。
在Linux上,在引导时对磁盘使用命令 hdparm-m16 -d1以启用同时读写多个扇区和 DMA功能文可以将响应时间提高 5~50%。
在 Linux上,用 async(默认)和 noatime挂载磁盘(mount)。对于某些特定应用,可以对某些特定表使用内存磁盘,但通常不需要。
优化操作系统
不要交换区。如果内存不足,增加更多的内存或配置你的系统使用较少内存。
不要使用 NFS 磁盘(会有 NFS 锁定的问题)。
增加系统和 MySQL服务器的打开文件数里。(在 safe mysqld脚本中加入 ulimit -n #)。
增加系统的进程和线程数里。
如果你有相对较少的大表,告诉文件系统不要将文件打碎在不同的磁道上(Solaris)。
使用支持大文件的文件系统(Solaris)。
选择使用哪种文件系统。在 Linux 上的 Reiserfs 对于打开、读写都非常快。文件检查只需几秒种
选择应用编程接口
PERL:
可在不同的操作系统和数据库之间移植。
适宜快速原型。
应该使用 DBI/DBD 接口。
PHP:
比 PERL易学。
使用比 PERL少的资源。
通过升级到 PHP4可以获得更快的速度。
C
MySQL的原生接口。
较快并赋予重多的控制。
低层,所以必须付出更多。
C++:
较高层次,给你更多的时间来编写应用。
仍在开发中
ODBC:
运行在 Windows和 Unix上。
几乎可在不同的 SQL服务器间移植。
较慢。MyODBC 只是简单的直通驱动程序,比用原生接口慢 19%。
有很多方法做同样的事。很难像很多 ODBC驱动程序那样运行,在不同的领域还有不同的错误。
问题成堆。Microsot偶尔还会改变接口。
不明朗的未来。(Microsoft 更推崇 OLE而非 ODBC)
JDBC:
理论上可在不同的操作系统何时据库间移植。
可以运行在 web客户端。
Python和其他:
可能不错,可我们不用它们。
优化应用
应该集中精力解决问题
在编写应用时,应该决定什么是最重要的:
速度
操作系统间的可移植性
SQL服务器间的可移植性
使用持续的连接。
缓存应用中的数据以减少 SQL服务器的负载
不要查询应用中不需要的列。
不要使用 SELECT * FROM table name..测试应用的所有部分,但将大部分精力放在在可能最坏的合理的负载下的测试整体应用。通过以一种模块化的方式进行,你应该能用一个快速”哑模块”替代找到的赢颈,然后很容易地标出下一个瓶颈。如果在一个批处理中进行大量修改,使用 LOCK TABLES。例如将多个 UPDATES或 DELETES集中在一起。
应该使用可移植的应用
Perl DBI/DBD
ODBC
JDBC
Python(或其他有普遍 SQL接口的语言)
你应该只使用存在于所有目的 SQL 服务器中或可以很容易地用其他构造模拟的 SQL 构造。
www.mysgl.com上的Crash-me页可以帮助你。
为操作系统/SQL 服务器编写包装程序来提供缺少的功能。
如果你需要更快的速度
应该找出瓶颈(CPU、磁盘、内存、SQL 服务器、操作系统、API 或应用)并集中全力解决。
使用给予你更快速度/灵活性的扩展。
逐渐了解 SQL 服务器以便能为你的问题使用可能最快的 SQL造并避免瓶颈。
优化表布局和查询。
使用复制以获得更快的选择(select)速度。
如果你有一个慢速的网络连接数据库,使用压缩客户/服务器协议。
不要害怕时应用的第一个版本不能完美地移植,在你解决问题时,你总是可以在以后优化它。
优化 MySQLD
挑选编译器和编译选项。
为你的系统寻找最好的启动选项
通读MySQL参考手册并阅读 Paul DuBios的《MySQL》一书。(已有中文版-译注)多用 EXPLAIN SELECT、SHOW VARIABLES、SHOW STATUS 和 SHOW PROCESSLIST了解查询优化器的工作原理。
优化表的格式。
维护你的表(myisamchk、CHECK TABLE、OPTIMIZE TABLE)
使用 MySQL的扩展功能以让一切快速完成。
保持数据为数据库第三范式,但不要担心冗余信息或这如果你需要更快的速度,创建总结表
在大表上不做 GROUP BY,相反创建大表的总结表并查询它。
UPDATE table set count=count+1 where key column=constant 非常快。
对于大表,或许最好偶尔生成总结表而不是一直保持总结表。
充分利用INSERT的默认值。
重要的 MySQL 启动选项
back log 如果需要大量新连接,修改它。
thread cache size 如果需要大量新连接,修改它
key buffer size 索引页池,可以设成很大。
bdb cache size BDB 表使用的记录和键吗高速缮存。
table cache 如果有很多的表和并发连接,修改它。
delay key write 如果需要缓存所有键码写入,设置它。
log slow queries 找出需花大量时间的查询
max heap table size 用于 GROUP BY
sort buffer 用于 ORDER BY和GROUP BY
myisam sort buffer size 用于 REPAIR TABLE
join buffer_size 在进行无键吗的联结时使用。
优化表
MySQL拥有一套丰富的类型。你应该对每一列尝试使用最有效的类型。
ANALYSE 过程可以帮助你找到表的最优类型: SELECT* FROM table name PROCEDUREANALYSE0。
对于不保存 NULL值的列使用 NOT NULL,这对你想索引的列尤其重要。
将ISAM类型的表改为 MYISAM。
如果可能,用固定的表格式创建表
不要索引你不想用的东西。
利用 MySQL能按一个索引的前缀进行查询的事实。如果你有索引 INDEX(a,b),你不需要在a上的
素引
不在长 CHAR/VARCHAR 列上创建索引,而只索引列的一个前缀以节省存储空间。CREATE TABLEable name (hostname CHAR(255) not null index(hostame(10)))对每个表使用最有效的表格式。
在不同表中保存相同信息的列应该有同样的定义并具有相同的列名。
.MySQL如何次存储数据
数据库以目录存储。
表以文件存储。
列以变长或定长格式存储在文件中。对 BDB表,数据以页面形式存储。
支持基于内存的表。
数据库和表可在不同的磁盘上用符号连接起来。
在 Windows上,MySQL 支持用.sym文件内部符号连接数据库
MySQL表类型
HEAP表:固定行长的表,只存储在内存中并用 HASH 索引进行索引。
ISAM表: MySQL 3.22中的早期 B-tree表格式。
MyIASM: IASM 表的新版本,有如下扩展:
二进制层次的可移植性。
NULL列索引。
对变长行比ISAM表有更少的碎片。
支持大文件。
更好的索引压缩。
更好的键吗统计分布。
更好和更快的 auto increment处理。
来自 Sleepcat的 Berkeley DB(BDB)表: 事务安全(有 BEGIN WORK/COMMITIROLLBACK)
MySQL行类型 (专指 IASM/MYIASM 表)
如果所有列是定长格式(没有 VARCHAR、BLOB或 TEXT),MySQL 将以定长表格式创建表,否则表以动态长度格式创建。
定长格式比动态长度格式快很多并更安全
动态长度行格式一般占用较少的存储空间,但如果表频繁更新,会产生碎片。
在某些情况下,不值得将所有 VARCHAR、BLOB和TEXT 列转移到另一个表中,只是获得主表上的重快速度。
-利用 myiasmchk(对ISAM,pack_iasm),可以创健只读压缩表,这使磁盘使用率最小,但使用量速磁盘时,这非常不错。压缩表充分地利用将不再更新的日志表
MySQL缓存
1、MySQL 高速缓存《所有线程共享,一次性分配)
、MySQL 高速缓存《所有线程共享,一次性分配)
键码缓存: key_buffer_size,默认 8M。
表缓存: table cache,默认 64。
线程缓存: thread cache size,默认 0
主机名缓存:可在编译时修改,默认 128。
内存映射表:目前仅用于压缩表。
注意: MySQL没有运行高速缓存,而让操作系统处理
2、MySQL 缓存区变量《非共享,按需分配)
sort buffer: ORDER BYIGROUP BY
record buffer: 扫描表。
join buffer _size: 无键联结
myisam sort buffer size: REPAIR TABLE
net buffer length:对于读 SQL语句并缓存结果。
tmp table size: 临时结果的HEAP表大小。
MySQL表高速缓存工作原理
每个MyISAM表的打开实例(instance)使用一个索引文件和一个数据文件如果表被两个线程使用或在同一条查询中使用两次,MyIASM将共享索引文件而是打开数据文件的另一个实例。如果所有在高速缓存中的表都在使用,缓存将临时增加到比表缓存尺寸大些。如果是这样,下一个被泽放的表将被关闭。
你可以通过检查 mysqld的 Opened tables变里以检查表缓存是否太小。如果该值太高,你应该增大表高速缓存。
MySQL扩展/优化-提供更快的速度
使用优化的表类型《HEAP、MyIASM 或 BDB表)。
对数据使用优化的列。
如果可能使用定长行。
使用不同的锁定类型《SELECT HIGH PRIORITY,INSERT LOW_PRIORITY)
Auto incremnent
REPLACE (REPLACE INTO table name VALUES (...))
INSERT DELAYED
LOAD DATA INFILE I LOAD FILEO
使用多行INSERT一次插入多行。
SELECT INTO OUTFILE
LEFT JOIN, STRAIGHT JOIN
LEFT JOIN ,结合IS NULL
ORDER BY 可在某些情况下使用键码。
如果只查询在一个索引中的列,将只使用索引树解决查询。
联结一般比子查询快(对大多数 SQL 服务器亦如此)。
LIMIT
SELECT * fromtable1 WHERE a > 10 LIMIT 10,20DELETE * fromtable1 WHERE a > 10 LIMIT 10
foo IN (常数列表)高度优化。
GET LOCKO/RELEASE LOCKO
LOCK TABLES
INSERT和SELECT 可同时运行。
UDF函数可装载进一个正在运行的服务器。
压缩只读表。
CREATE TEMPORARY TABLE
CREATE TABLE.SELECT
带 RAID 选项的 MyIASM 表将文件分割成很多文件以突破某些文件系统的 2G 限制。
Delay_keys
复制功能
MySQL何时使用索引
MySQL何时使用索引
对一个键码使用>,>=,=,<,<=,IF NULL和 BETWEEN
SELECT * FROM table name WHERE key _part1=1 and key _part2 > 5SELECT * FROM table name WHERE key part1 IS NULL:
当使用不以通配符开始的 LIKE
SELECT * FROM table name WHERE key part1 LIKE jani%
在进行联结时从另一个表中提取行时
SELECT * fromt1,t2 where t1.col=t2.key_part
找出指定索引的 MAX()或 MINO值
SELECT MIN(key_part2),MAX(key part2) FROM table name where key part1=10
个键码的前级使用 ORDER BY 或 GROUP BY
SELECT * FROM fOo ORDER BY key part1,key part2,key part3
在所有用在查询中的列是键码的一部分时间
SELECT key_part3 FROM table name WHERE key part1 1
MySQL何时不使用索引
如果 MySQL能估计出它将可能比扫描整张表还要快时,则不使用索引。例如如果 key_pat1均匀分布在 1和 100之间,下列查询中使用索引就不是很好:
SELECT * FROM table name where key part1 > 1 and key part1 < 90
如果使用 HEAP表且不用一搜索所有键码部分。
在HEAP表上使用 ORDER BY。
如果不是用键码第一部分
SELECT * FROM table name WHERE key part2= 1
如果使用以一个通配符开始的 LIKE
SELECT * FROM table name WHERE key part1 LIKE jani%
搜索一个索引而在另一个索引上做 ORDER BY
SELECT * fromtable name WHERE key part1 = # ORDER BY key2
学会使用 EXPLAIN
aGk22:7 008 10202006
mysql> explain select t3.DateOfAction, t1.TransactionID
-> from t1 join t2 join t3
-> where t2.ID = t1.TransactioniD and t3.ID = t2.GroupID
> order by t3.DateofAction, t1.TransactioniD:
table] type I possible keys key key len ref rowsI Extra
ALLNULLNULLNULLI NULLt1
11 Using temporary: Using filesort
t2 ref IDD4 t1TransactionID13
t3 l eq refI PRIMARY PRIMARY41t2.GroupID1
ALL和范围类型提示一个潜在的问题。
学会使用 SHOW PROCESSLIST
使用 SHOW processlist来发现正在做什么:
I db I Command Time state InfoId I User l Host
6 monty | localhost | bp Query 15 Sending data select * from station,station as s1
8 monty localhost Query0show processlist
oe2cosnss.ensnenencegenosjossneeneacnoenes..0.00-00000000000060000-000000000
在mysql或 mysqladmin 中用 KILL 来杀死掉的线程。
如何知晓 MySQL解决一条查询
运行项列命令并试图弄明白其输出:
SHOW VARIABLES;
SHOW COLUMNS FROM ... G
EXPLAIN SELECT ... G
FLUSH STATUS;
SELECT ...
SHOW STATUS
MySQL非常不错
日志
在进行很多连接时,连接非常快。
同时使用 SELECT 和INSERT的场合。
在不把更新与耗时太长的选择结合时。
在大多数选择/更新使用唯一键码时。
在使用没有长时间冲突锁定的多个表时。
在用大表时(MySQL使用一个非常紧凑的表格式)。
MySQL 应避免的事情
用删掉的行更新或插入表,结合要耗时长的 SELECT。
在能放在 WHERE子句中的列上用 HAVING。
不使用键码或键码不够唯一而进行 JOIIN。
在不同列类型的列上JOIN。
在不使用=匹整个键码时使用 HEAP表
在MySQL监控程序中忘记在 UPDATE或 DELETE 中使用一条 WHERE子句。如果想这样做,使用mysgl客户程序的--i-am-a-dummy选项。
MySQL 各种锁定
内部表锁定
LOCK TABLES《所有表类型适用)
GET LOCKOVRELEASE LOCKO
页面锁定(对 BDB 表)
ALTER TABLE 也在 BDB表上进行表锁定
LOCK TABLES允许一个表有多个读者和一个写者
一般 WHERE 锁定具有比 READ 锁定高的优先级以避免让写入方干等。对于不重要的写入方,可以使用 LOW_PRIORITY 关键字让锁定处理器优选读取方。
UPDATE LOW PRIORITY SET value 10 WHERE id 10:
给 MySQL 更多信息以更好地解决问题的技巧
注意你总能去掉(加注释)MySQL功能以使查询可移植:
SELECT ! SOL BUFFER RESULTS ..
SELECT SOL BUFFER RESULTS ..
将强制 MySQL 生成一个临时结果集。只要所有临时结果集生成后,所有表上的锁定均被释放。这能在遇到表锁定问题时或要花很长时间将结果传给客户满时有所帮助。
SELECT SOL SMALL RESULT ... GROUP BY ...
告诉优化器结果集将只包含很少的行。
SELECT SOL BIG RESULT ... GROUP BY ...
告诉优化器结果集将包含很多行。
SELECT STRAIGHT JOIN ...
强制优化器以出现在 FROM 子句中的序联结表。
SELECT ... FROM table name [USE INDEX (index list) I IGNORE INDEX (index list)able name2
强制MySQL使用/忽略列出的索引。
分支主题
事务的例子
MyIASM 表如何进行事务处理:
mysgl> LOCK TABLES trans READ,customer WRITE:
mysgl> select sum(value) from trans where customer id=some id:
mysql> update customer set total value=sum from previous statement
where customner id=some id:
mysql> UNLOCK TABLES:
BDB 表如何进行事务:
mysgl> BEGIN WORK;
mysql> select sum(value) from trans where customer id=some id:
mysql> update customer set total value=sum from previous statementwhere customner id=somne id:mysql> COMMIT;
注意你可以通过下列语句回避事务:
UPDATE customer SET value=value+new value WHERE customer id some id
使用 REPLACE 的例子
REPLACE 的功能极像INSERT,除了如果一条老记录在一个唯一索引上具有与新纪录相同的值,那么老记录在新纪录插入前则被删除。不使用
SELECT 1 FROM t1 WHERE key #
IF found-row
LOCK TABLES t1
DELETE FROM t1 WHERE key1=#
INSERT INTO t1 VALUES (..)
UNLOCK TABLES t1:
ENDIF
而用
REPLACE INTO t1 VALUES (...)
一般技巧
使用短主键。联结表时使用数字而非字符串。
当使用多部分键码时,第一部分应该时最常用的部分。
有疑问时,首先使用更多重复的列以获得更好地键码压缩。
如果在同一台机器上运行 MySQL客户和服务器,那么在连接 MySQL 时则使用套接字而不是TCP/IP(这可以提高性能 7.5%)。可在连接 MySQL 服务器时不指定主机名或主机名为localhost来做到。如果可能,使用--skip-locking(在某些 0S 上为默认),这将关闭外部锁定并将提高性能。
使用应用层哈希值而非长键码:
SELECT * FROM table name WHERE hash=MD5(concat(col1,col2)) ANDcol 1='constant' AND col 2=constant
在文件中保存需要以文件形式访问的 BLOB,在数据库中只保存文件名。
删除所有行比删除一大部分行要快。
如果SQL不够快,研究一下访问数据的较底层接口
执行流程
1、查询缓存
2、解析器生成解析树
3、预处理再次生成解析树
4、查询优化器
5、查询执行计划
6、查询执行引擎
7、查询数据返回结果
执行计划与执行明细
1,Explain:可以让我们查看MYSQL执行一条SQL所选择的执行计划;
2,Profiling:可以用来准确定位一条SQL的性能瓶颈;
binlog
sync_binlog
执行几次事务将binlog刷新到磁盘
sync_binlog选项控制mysql怎么刷新二进制日志到磁盘,默认是0,意味着mysql并不刷新,由操作系统自己决定什么时候刷新缓存到持久化设置,如果
这个值比0大,它指定了两次刷新到磁盘的动作之间间隔多少次二进制日志写操作
为 0
表示MySQL不控制binlog的刷新,由文件系统自己控制它的缓存的刷新。这时候的性能是最好的,但是风险也是最大的。
因为一旦系统Crash,在binlog_cache中的所有binlog信息都会被丢失
Mysql binlog日志有三种格式
Statement:每一条会修改数据的sql都会记录在binlog中
不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能
由于记录的只是执行语句,为了这些语句能在slave上正确运行,因此还必须记录每条语句在执行的时候的一些相关信息,以保证所有语句能在slave得到和在master端执行时候相同 的结果。另外mysql 的复制,像一些特定函数功能,slave可与master上要保持一致会有很多相关问题(如sleep()函数, last_insert_id(),以及user-defined functions(udf)会出现问题).
2)Row:不记录sql语句上下文相关信息,仅保存哪条记录被修改
Mixedlevel: 是以上两种level的混合使用
一般的语句修改使用statment格式保存binlog,如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种.新版本的MySQL中队row level模式也被做了优化,并不是所有的修改都会以row level来记录,像遇到表结构变更的时候就会以statement模式来记录。至于update或者delete等修改数据的语句,还是会记录所有行的变更。
架构
分表
水平拆分
垂直拆分
分库
MyCat中间件
读写分离
MySQL Proxy
表结构对性能的影响
1、冗余数据的处理(可以提高系统的整体查询性能)
1、每一列只能有一个值
2、每一行可以被唯一的区分
3、不包含其他表的已包含的非关键信息
2、大表拆小表
1、一般不会设计属性过多的表
2、一般不会超过500到1000万数据的表
3、有大数据的列单独拆为小表
3、根据需求展示更加合理的表结构
4、常用属性分离为小表
语句
慢查询
慢查询日志
show global status like '%slow%';
long_query_time=2
slow_query_log=ON
#log_queries_not_using_indexes=ON
把所有没有用到索引的查询都记录进来
slow_query_log_file=/root/mysql/data/slow.log
explain
字段说明
select_type
取值
SIMPLE
简单表,不使用表连接及子查询
PRIMARY
主查询,即外层的查询
UNION
UNION中的第二个或者后面的查询语句
SUBQUERY
子查询中的第一个SELECT
type
取值
ALL > index > range > ref > eq_ref > const,system > NULL
性能从左到右,性能由最差到最好
说明
ALL
语句:
EXPLAIN SELECT * FROM js_user ;
效果:
分支主题
全表扫描,MySQL遍历全表获取数据。
index
语句:
EXPLAIN SELECT id FROM js_user ;
效果:
分支主题
索引全扫描,MySQL遍历整个索引来匹配数据
range
语句:
EXPLAIN SELECT id FROM js_user WHERE id < 3;
效果:
分支主题
索引范围扫描
ref
使用非唯一索引扫描或唯一索引的前缀扫描
eq_ref
类似ref,区别就在于使用的索引是唯一索引
const/system
单表中最多有一个匹配行,查询起来非常迅速,所以这个匹配行中的其它列的值可以被优化器在当前查询中当作常量来处理。
NULL
MySQL 不用访问表或者索引,直接能够得到结果。
possible_keys
表示查询时可能使用的索引
key
表示实际使用的索引
key_len
使用到索引字段的长度
rows
扫描行的数量
Extra
执行情况的说明和描述,包含不适合在其他列中显示但是对执行计划非常重要的额外信息。
extended
show warnings
update 更新问题
相同的记录修改
binlog_format=statement和binlog_row_image=FULL时,InnoDB内部认真执行了update语句,即“把这个值修改成 (1,999)“这个操作,该加锁的加锁,该更新的更新
在binlog_format=row和binlog_row_image=FULL时,由于MySQL 需要在 binlog 里面记录所有的字段,所以在读数据的时候就会把所有数据都读出来,那么重复数据的update不会执行
数据采集与处理技术体系
一、采集流程
(一)确定目标
(二)选择工具
(三)发送请求
(四)解析内容
(五)存储数据
二、文本处理
(一)文本清洗
(二)文本转换
(三)文本分析
三、网页内容提取
(一)HTML/XML解析器
**lxml**:高效的HTML和XML解析库,支持XPath和CSS选择器。
**Beautiful Soup**:提供简洁的接口,方便解析HTML和XML文档。
**html5lib**:纯Python实现的HTML解析器,兼容HTML5标准。
(二)选择器
**XPath**:强大的节点选择语言,用于在XML文档中查找信息。
**CSS选择器**:基于HTML元素的类、ID等属性进行选择。
四、浏览器自动化与反爬
(一)浏览器自动化工具
**Selenium**:支持多种浏览器,可模拟用户操作。
**Playwright**:由微软开发,功能强大,支持浏览器自动化和测试。
(二)反爬策略
**设置请求头**:模拟正常浏览器请求,避免被识别为爬虫。
**使用代理IP**:隐藏真实IP地址,避免被封禁。
**控制请求频率**:避免短时间内大量请求触发反爬机制。
五、特定格式文件处理
(一)PDF文件
**PyPDF2**:用于PDF文件的读取、合并、分割等操作。
**pdfminer**:从PDF文档中提取文本和信息。
(二)Word文档
**python-docx**:创建和修改Microsoft Word文档。
(三)Excel文件
**openpyxl**:读取和写入Excel 2010 xlsx/xlsm文件。
**pandas**:提供高效的DataFrame结构,方便数据处理和分析。
六、自然语言处理
(一)分词与词性标注
**jieba**:中文分词工具,支持多种分词模式。
**hanlp**:功能全面的自然语言处理工具包,包括分词、词性标注等。
(二)命名实体识别
(三)情感分析
数据预处理、特征提取与降维技术
数据预处理
标准化
**StandardScaler**: 零均值单位方差的标准化。
**MinMaxScaler**: 将数据缩放到给定范围,通常是[0, 1]。
**MaxAbsScaler**: 将数据缩放到[-1, 1]范围,适用于稀疏数据。
**RobustScaler**: 对离群点鲁棒的标准化方法。
正则化
**Normalizer**: 将样本向量规范化为单位范数。
二值化
**Binarizer**: 将数据二值化,通常用于将连续特征转换为二进制特征。
编码
**OneHotEncoder**: 将类别特征转换为独热编码。
**OrdinalEncoder**: 将类别特征转换为整数编码。
缺失值处理
**SimpleImputer**: 用均值、中位数或众数填充缺失值。
**KNNImputer**: 使用K近邻算法填充缺失值。
多项式特征生成
**PolynomialFeatures**: 生成多项式和交互特征。
自定义转换
**FunctionTransformer**: 从函数创建转换器。
**自定义转换器**: 根据需要创建自定义的转换器。
特征提取
文本特征提取
**CountVectorizer**: 将文本转换为词频矩阵。
**TfidfVectorizer**: 常用的文本特征提取方法,结合了TF-IDF。
**TfidfTransformer**: 将词频矩阵转换为TF-IDF特征。
**HashingVectorizer**: 使用哈希技巧处理大语料库,节省内存。
图像特征提取
**局部二值模式 (LBP)**: 提取图像的纹理特征。
**尺度不变特征变换 (SIFT)**: 提取图像的局部特征。
**方向梯度直方图 (HOG)**: 提取图像的形状特征。
特征构建
**特征组合**: 通过组合现有特征创建新特征。
**特征转换**: 对特征进行数学变换,如对数变换、平方根变换等。
特征选择
Filter方法
**方差选择法**: 去除低方差的特征。
**相关系数法**: 选择与目标变量相关性高的特征。
**卡方检验**: 选择与目标变量独立性弱的特征。
**互信息法**: 选择与目标变量互信息高的特征。
Wrapper方法
**递归特征消除法 (RFE)**: 递归地移除特征并训练模型,选择最优的特征子集。
Embedded方法
**基于惩罚项的特征选择法**: 如Lasso回归中的L1正则化。
**基于树模型的特征选择法**: 如随机森林、梯度提升树中的特征重要性。
降维
主成分分析 (PCA)
**线性降维方法**: 通过找到数据中的主要变化方向来降低数据维度。
线性判别分析 (LDA)
**监督式降维方法**: 通过最大化类间距离和最小化类内距离来降低数据维度。
其他降维方法
**t-SNE**: 非线性降维方法,适用于高维数据的可视化。
**UMAP**: 一种新兴的非线性降维方法,适用于高维数据的可视化和降维。
一致性图谱
参观锁
乐观锁
行级锁
分布式锁
分片排队
CAS
对热点分布法之
锁
一致性
传统一致性
强一致性
Atomic
Consistency
Isolation
Duration
一致性
高一级一致性算法
高级一致性算法
一致性hash
理论
CAP
BASE
两阶段提交协议
协议
三阶段提交协议
TCC(柔性事务)
查询模式
补偿模式
最终一致性
异步偏保模式
消息偏保模式
校对模式
超时模式
快速失败
补偿模式
0 条评论
下一页