高性能mysql 笔记

高性能mysql 笔记

读锁是共享的,写锁是排他的,一个写锁会阻塞其他的写锁和读锁,确保在给定时间只有一个写入,防止其他用户的读取。
加锁消耗资源,mysql中由存储引擎实现自己的锁策略和锁粒度。
表锁,对表进行写操作前需要获得写锁,会阻塞其他用户的所有读写操作
行级锁最大程度支持并发处理,也带来最大的锁开销。
事务,一个独立的工作单元,要么全部执行成功,要么全部失败。
事务的acid特性:
原子性,一个事务是一个不可分割的最小工作单元,要么全部执行成功,要么全部失败回滚
一致性, 数据库总是从一个一致性的状态转换到另一个一致性的状态
隔离性,通常来说,一个事务的修改在最终提交之前对其他事务是不可见的,但根据隔离级别有例外
持久性,一旦事务提交,其所做的修改就会永久保存到数据库中,即使系统崩溃,修改的数据也不会丢失。持久性有点模糊,因为也分很多级别

隔离级别
每一种级别规定了一个事务的修改在事务内和事务间是否可见
read uncommitted, 未提交读,事务中的修改即使未提交,对其他事务也是可见的,很少使用
read committed, 提交读,一个事务开始时,只能看到已经提交的事务的修改,该事务在提交之前,所做的修改对其他事务不可见,不可重复读,因为每次读取都是读取最新的提交
repeatable read, 可重复读,解决了脏读的问题,在同一个事务多次执行同样的查询,结果是一致的,读取事务开始时的状态,会有幻读现象
串行执行, 强制事务串行执行,不能并发

mysql默认的是可重复读级别。

多版本并发控制

可以认为MVCC是行级锁的一个变种,在很多情况下避免了加锁操作,因此开销更低。
InnoDB的MVCC是通过在每行记录后面保存两个隐藏的列来实现的。这两个列一个记录了行的创建时间,一个记录了行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。下面看一下在REPEATABLE READ隔离级别下,MVCC具体是如何操作的。

SELECT

InnoDB会根据一下两个条件检查每行记录:
a. InnoDB只查找版本号小于等于当前事务版本号的数据行。这样可以确保事务读取的行,要么是事务开始前存在的,要么是事务自身插入或修改的。
b. 行的删除版本要么未定义,要么大于当前事务版本号。这样确保事务读取的行,在事务开始之前未被删除。

INSERT

InnoDB为新插入的每一行保存当前系统版本号作为行版本号。

DELETE

InnoDB为删除的每一行保存当前系统版本号作为行删除标识。

UPDATE

InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。

保存这两个额外系统版本号,使大多数读操作都可以不用加锁。这样设计使得读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行。不足之处是需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作。

基准测试 和 性能剖析部分, 略读了,暂不打算深入。

整数类型比字符串类型快
decimal(18,9)表示小数点前9个数字,小数点后面9个数字。
orm可能造成性能噩梦,因为可能生成不高效的数据类型及长度。

范式,每个事实数据出现并且只出现一次;反范式,信息是冗余的,可能存储在多个地方。

范式化的优点: 更新通常较快,因为数据没有重复,只需要少量修改,表通常更小,可以更好的放在内存中,因此操作会比较快;
缺点:通常需要关联,范式化将列存在不同的表中

反范式化,所有数据在一张表中,很好的避免关联

mysql alter table, 大部分的操作是锁表用新的结构创建空表,然后导入数据,删除旧表,并重命名新表。

schema设计原则:
使用小而简单的合适数据类型,避免使用null值
尽量使用整型定义标识列

索引优化是对查询性能优化最重要的手段

前缀索引是使索引更小,更快的有效方法,但mysql无法使用前缀索引做order by和group by.

联合索引时列的顺序很重要,因为是先按最左列进行排序,然后第二列。有一个经验是将选择性最高的列放在索引最左边,但具体应以查询条件来考虑。

聚簇索引并不是一种单独的索引类型,而是一种数据存储方式,数据行存储在叶子页上。

二级索引的叶子节点存储了主键值,通过主键值再到聚簇索引中找到数据行。

使用innodb时应该尽可能按主键顺序插入数据,尽可能使用单调增加的聚簇键的值来插入新行。

一个索引包含或者说覆盖所有需要查询的字段的值,称之为覆盖索引。
覆盖索引好处: 索引条目通常远小于数据行大小,能极大减少数据访问量,更容易全部放入内存

mysql有两种生成有序结果的方式:通过排序操作或者按索引顺序扫描。只有当索引的列顺序和order by子句的顺序完全一致,并且所有列的排序方向(倒序或正序)都一样时,mysql才能用索引来对结果排序。

增加新索引会导致insert,update,delete等操作变慢。

索引可以让查询锁定更少的行,索引能减少innodb访问的行数,从而减少锁的数量。

innodb在二级索引上使用读锁,但访问主键需要写锁

在线约会网站的例子 索引案例学习

对于组合索引,查询只能使用索引的最左前缀,直到遇到第一个范围条件列,范围查询无法使用索引

age列一般放在索引最后,就是因为多半是范围查询
(sex, country, age), 把sex列放在最前因为几乎所有查询都会用到sex列,即使不用,也可以在查询中使用 sex in (m, f)来让mysql选择该索引

todo: 对于排序后的结果,翻页到比较靠后时查询会很慢,解决办法:限制用户能够翻页的数量

查询性能优化

向mysql发送一个请求,发生了什么
1.客户端发送一条查询给服务器

  1. 服务器先检查缓存,如果命中,立即返回缓存中的结果,否则继续
  2. 服务端进行sql解析,预处理,再由优化器生成对应的执行计划
  3. mysql根据执行计划,调用存储引擎的API来执行查询
  4. 将结果返回给客户端

mysql客户端和服务器之间是半双工的,任何时刻,要么由服务器在向客户端发送数据,要么反之,不能同时发生。

count是一个特殊的函数,统计某个列值的数量或统计行数,统计列值时不统计null

优化limit分页,限制分页的数量;尽可能使用索引覆盖扫描,而不是查询所有的列,然后根据需要做一次关联再返回所需的列;有时候可以将limit查询转换为已知位置的查询;更好的设计时将具体的页数换成下一页按钮

mysql通过创建并填充临时表来进行union查询。除非确实需要服务器消除重复的行,否则union查询最好使用union all, 没有all, 会对整个临时表的数据做唯一性检查,代价非常高。

分区最大的优点是优化器可以根据分区函数来过滤一些分区,从而查询扫描更少的数据

视图,是一个虚拟表 todo 更多了解

innodb是mysql中唯一支持外键的内置存储引擎。某些场景,想确保两个相关表始终有一致的数据,外键约束使得查询需要额外访问别的表

触发器,在insert,update或者delete时,执行一些特定的操作,可以指定在执行前还是执行后触发

查询缓存系统会跟踪查询中涉及到的每个表,如果这些表发生变化,那么和这个表相关的缓存数据都将失效。 效率低 实现简单 代价小

第八章,服务器配置,第九章,操作系统和硬件优化 略过

一些释疑:
join的分类:
left join: 返回左表中所有的记录以及右表连接字段相等的记录;
right join,与left join 相反
inner join,内连接,或缩写为join,只返回两个表中连接字段相等的行
full join: 外连接,left join + right join

on 和 where
数据可在连接表时,会生成一张中间的临时表,然后将临时表返回给用户。使用on时是生成临时表的条件,生成的临时表已经是on条件过滤的结果;where是对生成的临时表的结果再进行过滤。因此一般on比where的查询效率要高。