mysql性能优化

mysql性能优化

  • 使用show status命令查询数据库性能参数
    • 常用参数包括slow_queries慢查询次数
    • com_(CRUD) CURD操作次数
    • Uptime 上线时间
  • 使用explain 查看sql执行计划,分析sql执行瓶颈
    • select_type表示查询类型
      • simple简单查询不包括连接查询和子查询
      • primary主查询 union表示连接查询 。。。。
    • table表示表名称
    • type是最重要的表示连接类型:首先说好点的情况
      • System 表仅有一行 const类型的特例一般不出现
      • const:表示数据表只有一个匹配行,通过主键查询时候出现
      • eq_ref:表示连接查询的时候对每个来自前面的表的行组合,只能匹配到这个表中的某一行,最好的连接查询类型,通过主键或者带索引的列关联时候出现。
      • ref查询条件既不是unique也不是主键,但是用了包含索引的列
      • ref_or_null 类似ref
      • 一下是不好的情况
      • index_merge 表示该连接类型,此时key列包含了使用的所有清单,key_len包含使用所有最长元素个数,也会产生笛卡尔基,基于key_len长度
      • range 检索指定范围的行,比如 in,< ,> 这些操作
      • index 和All差不多,比All快一点
      • ALL全表扫描,最差
    • possible_keys:表中所有索引列
    • key:查询中实际使用的索引列
    • key_len:mysql将实际使用的索引长度,也就是索引包含的数据量
    • ref:显示使用哪个列或者key一起从表中选择行
    • rows:执行sql时候必须查询的行数
    • extra
Distinct mysql发现第一个匹配行后,停止为当前的行组合搜索更多的行
Not exists MySql能够对查询进行LeftJoin优化,发现一个匹配LeftJoin标准的行后,不在为前面的行组合在改表内检查更多的行
range checked for each record MySql没有发现好的可以使用的索引,但发现如果来自签名的表的列
Using filesort MySql需要额外的一次传递,以找出如何按排序顺序检索行
Using index 从只使用索引树中的信息而不需要进一步搜索读取实际的行来检索表中的列信息。
Using temporary 为了解决查询,MySql需要创建一个临时表来容纳结果
Using where Where字句用于现在哪一个行匹配下一个或者发送到客
Using sort_union,Using union,Using intersect 这些函数说明如何为index_merge联接类型合并索引
Using index forgroup-by 类似访问表的Using index方式,标识MySql发现了一个索引,可以用来查询Group By或者Distinct查询的所有列,而不要额外搜索硬盘访问实际的表。
  • 使用like关键字查询情况:
    • 匹配字符串第一个字符是“%”,索引不起作用只有不再第一个位置索引才起作用
  • mysql可以使用联合索引:查询的时候条件中使用这些字段中第一个字段时候才生效
  • OR关键字查询:只有OR前后两个查询列都有索引时候,索引才会生效
  • 子查询优化:
    • 子查询的时候mysql需要创建一个临时表,查询完毕之后删除临时表,所以子查询比较慢,可以用连接查询,join 代替子查询,连接查询不用建立临时表
mysql数据结构优化
  • 数据字段很多的表分解成多个表,因为数据量很大的时候,会由于使用频率低的字段的存在而变慢
  • 曾加中间表提高联合查询的效率,解决频繁联合查询问题
  • 数据库设计应该遵循范式理论规约,尽可能减少冗余字段,但是这样表之间的关系就会很多,关联查询情况就变多, 所有合理的曾加冗余字段可以提高查询的速度
插入数据优化
  • 插入数据时候,影响插入速度的主要是索引,唯一性校验,一次插入的数据量等。
    • 插入数据的优化和存储引擎有关系,在mysql中常用的存储引擎有myISAM,InnoDB,两者的区别如下
      • MyISAM是非事务安全,InnoDB是事务安全的
      • MyISAM锁的粒度是表级别,InnoDB锁粒度是行
      • MyISAM支持全文类型索引,而InnoDB在mysql5.6上才支持全文索引。
        • 全文索引是为了解决模糊查询,因为like操作前缀是%是不走索引的
        • mysql全文索引长度默认是4,也就是2个汉字所以中文的情况下我们一般需要设置成8,这样四字成语也可以进索引了
      • MyISAM更简单,所以效率更高
      • MyISAM表是保存文件的形式,这样转移数据会更方便
  • MyISAM情况下:
  • 方法一: 在批量insert的时候先禁用索引,在insert之后在开启,这样索引的影响就消除了(空表不需要这样操作,因为没有数据)
  • 方法二:禁用唯一性检查
  • 方法三:insert语句应该用批量插入,而不是单个插入
  • 方法四:使用load data infile 命令直接通过文件导入
  • InnoDB情况下:
  • 方法一:禁用唯一性检查
  • 方法二:禁用对外键的检查
  • 方法三:禁止自动提交,之后在恢复
服务器硬件优化
  • 增大内存,内存IO比磁盘IO快很多
  • 用ssd
  • 做mysql集群,可以把IO分散到多台设备,减少资源竞争,提高并行能力
  • 配置多核处理器,因为mysql是多核处理器,有同时执行多个线程的能力
优化sql参数设置
  • Key_buffer_size:表示索引缓冲区的大小,曾加索引缓冲区的大小,可以更好处理索引,多索引读和多重写更优秀,但是增大上限制取决于内存大小。

  • table_cache:表示同时打开表的个数,越大,同时打个个数越多,但是太多会影响操作系统性能;

  • query_cache_size:表示缓冲区大小。和query_cache_type配合使用,值为0,所有查询不用缓冲区,但是不释放缓存,=1 时候使用缓存,但是查询时候用SQL_NO_CACHE除外,=2时候只在查询指定SQL_CACHE时候时候才使用

  • sort_buffer_size: 表示排序缓存区的大小,越大,排序越快

  • read_buffer_size:读取的内存

  • innodb_flush_log_at_trx_commit:

    • 表示什么时候将缓冲区的数据写入日志文件,并且将日志写入磁盘,非常重要的参数,有三个值,分别是0,1,2
    • 0的时候表示每隔1秒将数据写入日志文件并且将日志文件写入磁盘
    • 1表示每次提交事务时候讲数据写入日志文件并且将文件写入磁盘
    • 2表示每次提交事务将数据写入日志文件,每隔一秒将日志文件写入磁盘
    • 默认是1 安全性最高,但是每次提交事务都写磁盘比较费时,0 最快,安全性最低,2每一秒会写入,所以故障也只是丢失1~2秒的数据
  • Innodb_buffer_pool_size:表示innoDB类型的表和索引的最大缓存,这个值越大,查询速度就越快,但是这个值太大会影响操作系统性能。

Mysql事务

原子性:一个事务是一个小的工作单元,要么都成功要么都失败。

一致性:数据库总是从一个一致保持到另外一个一致,指一个事务提交之前和之后。

隔离性:通常来说一个事务做的修改对其他事务是不可见的。

持久性:事务提交之后修改会永久保留。

Mysql事务隔离级别
  • Read uncommitted :未提交读:
    • 事务中的修改即使没有提交,对其他事务也是可见的,也就是说,事务可以读取未提交的数据,这样就会出现脏读
  • Read Committed:提交读
    • 一个事务从开始到提交之前,所做的任何修改对其他事务都是不可见的,也叫做不可重复读,但是两次同样的查询在事务提交的前后分别执行,可能有不同的结果
  • Repeatable read:可重复读
    • 默认的级别,可重复读会产生幻读的情况,也就是所,当事务A读取某个范围内的记录时候,事务B在该范围内insert了一条数据,之前的事务在去读取这个纪录,会有B插入的数据,但是A是在同一个事务里面,这样称为幻读,通过MVCC可以解决
  • Serializable:可串行化
    • 也就是强制让所有事务串行执行,这样避免了幻读,但是却带来性能问题,会出现大量锁超时,锁竞争问题。
InnoDB常见的几种锁机制
  • 共享锁(S锁):允许事务获得锁后去读数据,一个事务获取S锁后允许其他事务获取S锁,此时两个事务都持有共享锁S,但是不允许其他事务获取X锁。
  • 独占锁(X锁):允许事务获得锁后去更新或者删除数据,如果一个事务获取的独占锁X,则不允许其他事务获取S锁或者X锁,必须等到改事务释放X锁之后才可以获取到。
    • 通过一下sql验证:
START TRANSACTION WITH CONSISTENT SNAPSHOT;
select * from QuestionAnswerContentDetailVerify_online where memberID = 105875593 lock in SHARE MODE; -- 共享锁
select * from QuestionAnswerContentDetailVerify_online where memberID = 105875593 for UPDATE; -- 独占锁
COMMIT;

START TRANSACTION WITH CONSISTENT SNAPSHOT;
SELECT * FROM QuestionAnswerContentDetailVerify_online WHERE memberID = 105875593 lock in SHARE mode; -- 共享锁
UPDATE QuestionAnswerContentDetailVerify_online set answerContentDetail = 'U动漫' WHERE memberID = 105875593; -- 独占锁
COMMIT;
  • 意向锁(Intention Locks) ,上部分说明InnoDB支持行锁和表锁,意向锁就是表锁,用来指示接下来一个事务将要获取的是什么类型的锁(共享或者独占)。意向锁也分为意向共享(IS),意向独占(IX),依次表示接下来一个事务将获得共享锁,独占锁。易向锁不需要显示的获取,我们在获取共享锁或者独占锁之前会先自动获取意向共享,意向独占锁,也就是说我们需要现货区意向锁。意向锁不会锁住任何东西,除非有进行全表请求的操作,否则不会锁住任何数据,存在的意义只是用来标识有事务正在锁某一行的数据,或者将要锁某一行数据。
原文:Intention locks are table-level locks that indicate which type of lock (shared or exclusive) a transaction requires later for a row in a table.
  • 记录锁(record Locks),锁住某一行,如果表存在索引,那么记录锁是锁在索引上的,如果表没有索引,那么innoDB会创建一个隐藏的聚簇索引加锁。所以我们在进行查询的时候尽量采用索引进行查询,这样可以降低锁冲突。
  • 间隙锁(Gap Locks),间隙锁是一种记录行与记录行之间存在空隙或在第一行记录之前或最后一行记录之后产生的锁。间隙锁可能占据的单行,多行或者是空记录。通常的情况是我们采用范围查找的时候,比如在学生成绩管理系统中,如果此时有学生成绩 60,72,80,95,一个老师要查下成绩大于 72 的所有同学的信息,采用的语句是select * from student where grade > 72 for update,这个时候 InnoDB 锁住的不仅是 80,95,而是所有在 72-80,80-95,以及 95 以上的所有记录。为什么会 这样呢?实际上是因为如果不锁住这些行,那么如果另一个事务在此时插入了一条分数大于 72 的记录,那会导致第一次的事务两次查询的结果不一样,出现了幻读。所以为了在满足事务隔离级别的情况下需要锁住所有满足条件的行。
  • Next-Key Locks,NK 是一种记录锁和间隙锁的组合锁。是 4 和 5 的组合形式,既锁住行也锁住间隙。并且采用的左开右闭的原则。InnoDB 对于查询都是采用这种锁的。

案例:

drop TABLE if EXISTS question_test;
CREATE TABLE `question_test` (
  `q_id` int(11) UNSIGNED not null AUTO_INCREMENT,
	`q_uid` int(11) UNSIGNED DEFAULT NULL,
  `q_name` varchar(10) DEFAULT NULL,
  `q_part` varchar(10) DEFAULT NULL,
	primary KEY (q_id),
	KEY idx_q_uid (q_uid) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `zhenai_qa`.`question_test` (`q_id`, `q_uid`, `q_name`, `q_part`) VALUES (1,1, '问题一', '描述一');
INSERT INTO `zhenai_qa`.`question_test` (`q_id`, `q_uid`, `q_name`, `q_part`) VALUES (2,2, '问题二', '描述二');
INSERT INTO `zhenai_qa`.`question_test` (`q_id`, `q_uid`, `q_name`, `q_part`) VALUES (3,3, '问题三', '描述三');
INSERT INTO `zhenai_qa`.`question_test` (`q_id`, `q_uid`, `q_name`, `q_part`) VALUES (6,6, '问题四', '描述四');
INSERT INTO `zhenai_qa`.`question_test` (`q_id`, `q_uid`, `q_name`, `q_part`) VALUES (10,10, '问题十', '描述十');

select * from question_test

# T1
START TRANSACTION WITH CONSISTENT SNAPSHOT; -- 1
SELECT * FROM question_test WHERE q_uid = 6 for UPDATE; -- 2
COMMIT;  -- 5

# T2
START TRANSACTION WITH CONSISTENT SNAPSHOT;  -- 3
INSERT INTO question_test(q_uid) VALUES(11);
INSERT INTO question_test(q_uid) VALUES(5);  -- 4
INSERT INTO question_test(q_uid) VALUES(7);
INSERT INTO question_test(q_uid) VALUES(8);
INSERT INTO question_test(q_uid) VALUES(9);
SELECT * FROM question_test WHERE q_id = 6 for UPDATE;
COMMIT;
ROLLBACK;
  • 按照上面 1,2,3,4 的顺序执行会发现第 4 步被阻塞了,必须执行完第 5 步后才能插入成功。这里我们会很奇怪明明锁住的是uid=6 的这一行,为什么不能插入 5 呢?原因就是这里采用了 next-key 的算法,锁住的是(3,10)整个区间。感兴趣的可以试一下。
mysql MVCC 多版本并发控制
  • MVCC的实现是通过保存数据在某个时间点上的快照来实现的,也就是说,不管事务执行的时间多长,每个事务看到的数据都是一致的,但是每个事务开始的时间不同,就有可能每个事务对同一个表,看到的数据是不一样的。包括两种实现,乐观型并发控制,悲观型并发控制
    • 乐观型并发控制:InnoDB的MVCC是通过在每一行纪录后面都加上两个隐藏的列来实现,一个列保持行的创建版本号,一个表示删除版本号,每一个事务开始,系统版本号码自动递增,事务开始时候的系统版本号码就是事务的版本号码。用来和查询到的纪录的版本号码匹配,如下操作:
      • select 中InnoDB会更具两个条件筛选:
        1. InnoDB值查找版本<=当前系统版本号码的数据行,这样确保事务读取的行要么是在事务之前已存在,要买是事务自己插入的。
        2. 行的删除版本号码要么未定义,要么比当前的大,保证读到的行在事务开始之前未被删除。
      • insert: InnoDB为新插入每一行保持当前系统版本号码作为新版本号码
      • delete:InnoDB为删除的每一行保存当前系统版本号码作为行删除标示
      • Update:InnoDB为插入一行新的纪录,保存当前系统版本号码作为行版本号码,同时保存当前系统版本号码到原来的行作为删除标示。
  • 保存着两个额外的系统版本号码大多数读不需要加索,但是只使用read committed 和Repeatable read形式,因为Read UnCommitted总是读新数据,serializable 对所有读取行加索。
  • 悲观型并发控制也就是serializable
mySql索引
  • mysql的索引和存储引擎息息相关,即使是同样的索引,可能不同的存储引擎实现的方式就不一样如下:
    • myIsam索引的实现使用B+Tree 作为索引的结构,叶子节点的data域存放的是数据记录的地址,在myISAM中主索引和辅索引没有任何区别,只是主索引key唯一而已--------由此可得 MyISAM中索引检索算法首先按照B+Tree来搜索索引,如果指定key存在,取出器Data域的值,然后以Data的值作为地址,来查询相应地址的数据信息,---------------------(重要) myISam这种数据区域存储的是内存地址的方式我们称为聚簇索引
    • InnoDB也是B+Tree作为索引结构,但是区别是,在InnoDB中树的叶子节点上保存了完整的数据积累,并且内存空间连续叫做聚簇索引--------------区别一
    • InnoDB的数据文件和索引文件是同一个问题,myISAM是分开的----区别二
    • InnoDB一定要有主键,如果没有指定,择自动选一个可以为之标识的字段做主键,这个也没有的话,innoDB生一个隐含字段做主键
    • innoDB的所有辅索引的Data都是存储的对应主键的值,然后间接的使用索引。
B+树适合做索引原因

B+树适合作数据库的基础结构,完全是因为计算机的内存-机械硬盘测两层存储结构,内存可以快速完成随机访问(随机访问即是给出任意一个地址,要求返回这个地址的数据)但是容量小,硬盘的随机访问需要经过机械操作,比如磁头的寻址,还以磁盘的转动,效率会低很多,但是容量大。所以数据库容量可以大大超过内存,所以数据库的检索需要借助几次IO操作来完成,但是B+Tree的存储结构的数据离散的程度已经算很低了,所以他相比于红黑树(自平衡二叉查找树 )的结构来说可以减少很多次磁针的寻址,效率也会快很多。

你可能感兴趣的:(mysql)