1.mysql语句执行的步骤
- 客户端请求->连接器(验证用户身份,给与权限)
- 查询缓存(存在缓存则直接返回,不存在则执行后续操作)
- 分析器(对sql进行词法分析和语法分析操作)
- 优化器(主要对执行的sql优化选择最优的执行方案方法)
- 执行器(执行时会先看用户是否有执行权限,有才去使用这个引擎提供的接口)->去引擎层获取数据返回(如果开启查询缓存则会缓存查询结果)
2.mysql有哪几种锁
- 表级锁:开销小,加锁快,不会出现死锁,锁粒度大,发生锁冲突的概率最高,并发度最低
- 行级锁:开销大,加锁慢,会出现死锁,锁粒度最小,发生锁冲突的概率最低,并发度最高
- 页面锁:开销和加锁时间界于表锁和行锁之间,会出现死锁,
3.mysql中不同的表格
- myisam
- heap
- merge
- innodb
- isam
4.myisam和innodb的区别
myisam
- 不支持事务,但每次查询都是原子的
- 支持表级锁,即每次操作是对整个表加锁
- 存储表的总行数
- 一个Myisam表有三个文件:索引文件,表结构文件,数据文件
- 采用非聚集索引,索引文件的数据域存储指向数据文件的指针,辅索引与主索引基本一致,但是辅索引不用保证唯一性
innodb
- 支持acid事务,支持事务的四种隔离级别
- 支持行级锁及外键约束:因此可以支持写并发
- 不存储总行数
- 一个innodb引擎存储在一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分布在多个文件里)也有可能为多个,受操作系统文件大小的限制。
- 主键索引采用聚集索引,辅索引的数据域存储主键的值,因此从辅助索引查找数据,需要先通过辅索引找到主键值,再访问辅索引,最好使用自增主键,防止插入数据时,为维持B+树结构,文件的大调整。
5.四种隔离级别
- read uncommitted:读到未提交数据
- read committed:不可重复读
- repeatable read:可重复读
- serializable:串行
6.mysql数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化?
- 设计良好的数据库结构,允许部分数据冗余,尽量避免join查询,提高效率
- 选择合适的表字段数据类型和存储引擎,适当添加索引
- mysql库主从读写分离
- 找规律分表,减少单表中的数据量提高查询速度
- 添加缓存机制,比如memcached,apc等
- 不经常改动的页面,生成静态页面
- 书写高效率sql。
7.锁的优化策略
- 读写分离
- 分段加锁
- 减少锁持有时间
- 多个线程尽量以相同的顺序去获取资源
不能将锁的粒度过于细化,不然可能会出现线程的加锁和释放次数过多,反而效率不如一次加一把大锁
8.索引的底层实现原理和优化
B+树。主要是在所有叶子结点中增加了指向下一个叶子节点的指针,使用默认自增的主键作为主索引。
索引常见底层数据结构
哈希表,有序数据组,搜索树
- 哈希表:等值查询场景,eg:memcached和其他一些nosql引擎,不适合范围查询
- 有序数组索引只适用于静态存储引擎,等值和范围查询性能好,但更新数据成本高
- N叉树由于读写上的性能有点和适配磁盘访问模式以及广泛应用在数据库引擎中
- 扩展,
索引类型
- 主键索引:叶子节点存的整行数据,在innodb里也被称为聚簇索引
- 非主键索引:叶子节点存的主键的值,在innodb里也被称为二级索引
innodb B+树索引
考虑因素:
- innodb需要执行的场景和功能需要在特定查询上拥有较强的性能
- cpu将磁盘上的数据加载到内存中需要花费大量时间
选择原因
- 哈希索引虽然能提供O(1)复杂度查询,但对范围查询和排序却无法很好的支持,最终会导致全表扫描
- B树能够在非叶子节点存储数据,但会导致在查询连续数据可能带来更多的随机IO
- B+树的所有叶子节点可以通过指针来相互连接,减少顺序遍历带来的随机IO
- 由于唯一索引用不上change buffer的优化机制,如果业务可以接受,从性能角度出发建议优先考虑非唯一索引
9.覆盖索引和索引下推
- 覆盖索引
- 在某个查询里面,索引 k 已经“覆盖了”查询需求,称为覆盖索引。
- 覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。
- 索引下推
- MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
10.索引失效
- 对索引使用左或者左右模糊匹配,也就是 like %xx 或者 like %xx% 这两种方式都会造成索引失效。原因在于查询的结果可能是多个,不知道从哪个索引值开始比较,于是就只能通过全表扫描的方式来查询。
- 对索引进行函数/对索引进行表达式计算,因为索引保持的是索引字段的原始值,而不是经过函数计算的值,自然就没办法走索引。
- 对索引进行隐式转换相当于使用了新函数。
- WHERE 子句中的 OR语句,只要有条件列不是索引列,就会进行全表扫描。
11…优化mysql
- sql语句及索引的优化
- 数据库表结构的优化
- 系统配置的优化
- 硬件的优化
12.优化数据库
- 选取最适用的字段属性,尽可能减少定义字段宽度,尽量把字段设置NOTNULL,例如省份,性别最好适用ENUM
- 使用连接(join)来代替子查询
- 使用联合(union)来代替手动创建的临时表
- 事务处理
- 锁定表、优化事务处理
- 适用外键,优化锁定表
- 建立索引
- 优化查询语句
13.索引,主键,唯一索引,联合索引区别,对数据库性能有什么影响
索引是一种特殊的文件,包含对数据表里所有记录的引用指针。
- 普通索引的唯一任务就是加快对数据的访问速度。允许被索引的数据列包含重复的值,如果能确定某个数据列将只包含彼此各不相同的值,为这个数据列创建索引的时候就应该用关键字unique把它定义为一个唯一索引,即唯一索引可以保证数据记录的唯一性。
- 主键:一种特殊的唯一索引,在一张表中只能定义一个主键索引,用于唯一标识一条记录,primary key来创建
- 联合索引,索引可以覆盖多个数据列,eg:INDEX(columnA,columnB)索引
索引可以极大的提高数据的查询速度,但会降低插入、删除、更新表的速度,因此在执行这些写操作时,还要操作索引文件。
14.数据库事务
特性:原子性,一致性,隔离性,持久性
15.索引:
- 目的:
- 快速访问表的特定信息,提高检索速度
- 创建唯一性索引,保证数据表中每行数据的唯一性
- 加速表和表之间的链接
- 使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。
- 缺点:
- 创建索引和维护索引需要耗费时间,这个时间随数据量增加而增加
- 索引需要占用物理空间
- 当对表进行增删改的时候索引页需要动态维护,降低了数据的维护速度
- 建立原则
- 在最频繁使用的,用以缩小查询范围的字段上建立索引
- 在频繁使用的,需要排序的字段上建立索引
- 不适合建立索引
- 查询中很少涉及的列或者重复值比较多的列
- 对于特殊的数据类型不宜建立索引,egtextdeng
16.mysql外连接,内连接,自连接区别
-
交叉连接:笛卡尔积,不使用任何条件,直接将一个表的所有记录和另一个表中的所有记录一一匹配
-
内连接:只有条件的交叉连接,根据某个条件筛选出符合条件的记录,不符合条件的记录不会出现在结果集中,内连接只连接匹配的行
-
外连接:结果集中不仅包含符合连接条件的行,而且还会包含左表,右表或两个表中的所有数据行,依次称
- 左外链接:左连接,左表为主表,左表中所有记录都会出现在结果集中,对于右表中没有匹配的记录,仍要显示
- 右外连接:右连接,右表为主表,右表中所有记录都会出现在结果集中,
- 全外连接,mysql目前不支持全外连接
17.数据完整性指数据的精确性和可靠性
- 实体完整性:规定表中的每一行在表中是唯一的实体
- 域完整性:指表中的列必须满足某种特定的数据类型约束,其中约束包括取值范围、精度等规定
- 参照完整性:指两个表的主关键字和外关键字的数据应一致,保证了表之前的数据一致性,防止了数据丢失和无意义的数据在数据库中扩散
- 用户定义的完整性:不同的关系数据库系统根据其应用环境的不同,往往还需要一些特殊的约束条件,用户定义的完整性即是针对某个特定关系数据库的约束条件,反映了某一具体应用必须满足的语义要求
与表有关的约束为:包括列约束(not null) 表约束(primary key,foreign key,check,unique)
18.视图,游标
-
视图:一种虚拟的表,具有和物理表相同的功能,可以对视图进行增,改,查,操作,视图通常是有一个表或者多个表的行或列的子集,对视图的修改不影响基本表,使得我们获取数据更容易
-
游标:是对查询出来的结果集作为一个单元来有效的处理,游标可以定在该单元中的特定行,从结果集的当前行检索一行或多行,可以对结果集当前行做修改。一般不使用游标,但是需要逐条处理数据的时候,游标很重要。
19.存储过程
存储过程是一个预编译的sql语句,优点是允许模块化设计,只需要创建一次,以后在该程序中就可以调用多次,如果某次操作需要执行多次sql,使用存储过程比单纯sql语句执行要快,可以用一个命令对象来调用存储过程。
20.基本表,视图
基本表是本身独立存在的表,在sql中一个关系就对应一个表
视图是从一个或几个基本表导出的表,视图本身不独立存储在数据库中,是一个虚表
21.视图优点
- 视图能够简化用户的操作
- 视图使用户能以多种角度看待同一数据
- 视图为数据库提供了一定程度的逻辑独立性
- 视图能够对机密数据提供安全保护
22主键,外键,索引
- 主键:唯一标识一条记录,不能有重复的,不允许为空,—保证数据完整性 — 只能一个
- 外键:另一个表的主键,外键可以有重复的,可以是空值 — 用来和其他表建立联系用的 — 可以有多个外键
- 索引:没有重复值,但有个空值 — 提高查询排序的速度 — 可以有多个索引
23sql优化
- where子句:表之间的连接必须写在其他where条件之前,那些可以过滤掉最大数量记录的条件必须写在where子句的末尾having最后
- 用exists替代in,用not exists 替代not in
- 避免在索引列上使用计算
- 避免在索引上使用is null 和is not null
- 对查询进行优化,避免全表扫描,考虑在where及order by 涉及的列上建立索引
- 尽量避免在where子句中对字段进行null值判断,否则将导致引擎放弃使用索引而进行全表扫描
- 尽量避免在where子句中对字段进行表达式操作,将导致引擎放弃使用索引而进行全表扫描
索引
InnoDB 存储引擎的默认索引实现为:B+ 树索引。
自增主键:
使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页。如果使用非自增主键(如果身份证号或学号等),由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现有索引页得中间某个位置, 频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过OPTIMIZE TABLE(optimize table)来重建表并优化填充页面。
聚簇索引
聚簇索引就是按照每张表的 主键 构造一棵B+树,同时叶子节点中存放的就是整张表的行记录数据。
在 InnoDB 中,只有主键索引是聚簇索引,如果没有主键,则挑选一个唯一键建立聚簇索引。如果没有唯一键,则MySQL自动为InnoDB表生成一个隐含字段来建立聚簇索引,这个字段长度为6个字节,类型为长整形。
当查询使用聚簇索引时,在对应的叶子节点,可以获取到整行数据,因此不用再次进行回表查询。
- 聚簇索引:叶子节点存放的是主键值和数据行,支持覆盖索引(innodb的是主键索引使用的是聚簇索引)
- 非聚簇索引:叶子节点存放的是主键值或数据记录的地址(myisam使用的都是非聚簇索引)
根据主键索引搜索时,直到找到key所在的节点即可取出数据,根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。
非聚簇索引不一定会回表查询,如果查询语句全部命中索引,则不必再进行回表查询
索引底层实现
- hash索引
- 基于哈希表实现,只有精确匹配索引所有列的查询才有效,对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码(hash code),并且Hash索引将所有的哈希码存储在索引中,同时在索引表中保存指向每个数据行的指针。
- B+树索引:数据库索引所采用的存储结构。数据都在叶子节点上,并且增加了顺序访问指针,每个叶子节点都指向相邻的叶子节点的地址。相比B-Tree来说,进行范围查找时只需要查找两个节点,进行遍历即可。而B-Tree需要获取所有节点,相比之下B+Tree效率更高。
- n棵子tree的节点包含n个关键字,不用来保存数据而是保存数据的索引。
- 所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
- 所有的非终端结点可以看成是索引部分,结点中仅含其子树中的最大(或最小)关键字。
- B+ 树中,数据对象的插入和删除仅在叶节点上进行。
- B+树有2个头指针,一个是树的根节点,一个是最小关键码的叶节点。
使用b+树原因:B+树只要遍历叶子节点就可以实现整棵树的遍历,而且在数据库中基于范围的查询是非常频繁的,而B树只能中序遍历所有节点,效率太低。
创建的索引有没有被使用到?或者怎么才能知道这条语句运行很慢的原因
用explain命令查看语句的执行计划,mysql执行语句前会将该语句过一遍查询优化器,之后会拿到对语句的分析,即执行计划,其中包含许多信息,可以通过其中和索引相关的信息来分析是否命中了索引。
创建了索引但查询是并没有使用的情况
- 使用不等于查询
- 列参与了数学运算或者函数
- 在字符串 like 时左边是通配符。类似于’%aaa’。
- 当 mysql 分析全表扫描比使用索引快的时候不使用索引。
- 当使用联合索引,前面一个条件为范围查询,后面的即使符合最左前缀原则,也无法使用索引。
索引种类
- 普通索引:最基本的索引,它没有任何限制,值可以为空;仅加速查询
- 唯一索引:与普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。简单来说:唯一索引是加速查询 + 列值唯一(可以有null)
- 主键索引:一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。简单来说:主键索引是加速查询 + 列值唯一(不可以有null)+ 表中只有一个。
- 组合索引:指在多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合。
- 全文索引:用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。fulltext索引配合match against操作使用,而不是一般的where语句加like。它可以在create table,alter table ,create index使用,不过目前只有char、varchar,text 列上可以创建全文索引。值得一提的是,在数据量较大时候,现将数据放入一个没有全局索引的表中,然后再用CREATE index创建fulltext索引,要比先为一张表建立fulltext然后再将数据写入的速度快很多。
适合索引
- 字段有唯一性限制
- 经常用于where查询条件的字段
- 经常用于group by和order by的字段
不需要创建索引
- where,group by,order by 里用不到的字段
- 字段中存在大量重复数据,不需要创建索引
- 表数据太少,不需要创建索引
- 经常更新的字段不用创建索引
索引失效:
- 模糊查询:like ‘%’;like '%%'通配符放在开头会导致索引失效。会进行全表扫描
- 索引类使用函数:带有函数的索引列记录的是索引的原始值,不是计算后的值,所以mysql不会识别,在mysql8.0增加了函数索引,用来解决这一问题。
- 数据类型发生转换:当字符类型没有使用引号的时候,mysql会自动将字符转换成数字,会用到隐式转换,相当于使用了函数。(但是在8.0函数索引中会不会有效,还没研究)
- WHERE 子句中使用OR连接:当or前后都是索引列的时候不会导致索引失效,但当or前后有一个不是索引列的时候索引就会失效,原因是当使用or的时候,有一个条件不满足就会进行全面扫描。
- 多列组成组合索引:需要遵循最左匹配原则,从索引最左侧顺序进行检索,在组合索引中是按照列的顺序进行存储的,所以查的时候也需要按照顺序查。
- 索引列使用运算符,!=或者not:类似使用函数。
- 多表连接查询没有正确使用mysql规则,也会导致索引失效。
索引优化
- 前缀索引优化:使用某个字段中字符串的前几个字符建立索引
- 减小索引字段大小,可以增加一个索引页中存储的索引值,有效提高索引的查询速度。
- 一些大字符串的字段作为索引时,使用前缀索引可以减小索引项的大小。
- 局限性:order by 就无法使用前缀索引;无法把前缀索引用作覆盖索引;
- 覆盖索引优化:SQL 中 query 的所有字段,在索引 B+Tree 的叶子节点上都能找得到的那些索引,从二级索引中查询得到记录,而不需要通过聚簇索引查询获得,可以避免回表的操作。
- 主键索引最好是自增的;InnoDB 创建主键索引默认为聚簇索引,数据被存放在了 B+Tree 的叶子节点上。同一个叶子节点内的各个数据是按主键顺序存放的,每当有一条新的数据插入时,数据库会根据主键将其插入到对应的叶子节点中。
- 使用自增主键:每次插入的新数据就会按顺序添加到当前索引节点的位置,不需要移动已有的数据,当页面写满,就会自动开辟一个新页面。因为每次插入一条新记录,都是追加操作,不需要重新移动数据,因此这种插入数据的方法效率非常高。
- 使用非自增主键:由于每次插入主键的索引值都是随机的,因此每次插入新的数据时,就可能会插入到现有数据页中间的某个位置,这将不得不移动其它数据来满足新数据的插入,甚至需要从一个页面复制数据到另外一个页面,我们通常将这种情况称为页分裂。页分裂还有可能会造成大量的内存碎片,导致索引结构不紧凑,从而影响查询效率。
- 索引最好设置为not null
- 索引列存在 NULL 就会导致优化器在做索引选择的时候更加复杂,更加难以优化,因为可为 NULL 的列会使索引、索引统计和值比较都更复杂,比如进行索引统计时,count 会省略值为NULL 的行。
- NULL 值是一个没意义的值,但是它会占用物理空间,所以会带来的存储空间的问题,会导致更多的存储空间占用,因为 InnoDB 默认行存储格式COMPACT,会用 1 字节空间存储 NULL 值列表
- 防止索引失效:用上了索引并不意味着查询的时候会使用到索引,避免写出索引失效的查询语句,否则这样的查询效率是很低的。可以使用explain查看执行计划
事务相关
特性:
- A=Atomicity:原子性,就是要么全部成功,要么全部失败。不可能只执行一部分操作。
- C=Consistency:一致性,系统(数据库)总是从一个一致性的状态转移到另一个一致性的状态,不会存在中间状态。
- I=Isolation:隔离性,通常来说:一个事务在完全提交之前,对其他事务是不可见的.有例外情况。
- D=Durability:持久性,一旦事务提交,那么就永远是这样子了,哪怕系统崩溃也不会影响到这个事务的结果。
事务回滚机制:
恢复机制是通过回滚日志(undo log)实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后在对数据库中的对应行进行写入。当事务已经被提交之后,就无法再次回滚了。
作用:
- 能够在发生错误或者用户执行
ROLLBACK
时提供回滚相关的信息 - 在整个系统发生崩溃、数据库进程直接被杀死后,当用户再次启动数据库进程时,还能够立刻通过查询回滚日志将之前未完成的事务进行回滚,这也就需要回滚日志必须先于数据持久化到磁盘上,是我们需要先写日志后写数据库的主要原因。
数据库并发事务会带来的问题
- 脏读:A 事务读取到了 B 事务未提交的内容,但是之后B事务满足一致性等特性而做了回滚操作,那么读取事务得到的结果就是脏数据了。
- 幻读:A 事务读取了一个范围的内容,而同时 B 事务在此期间插入(删除)了一条数据。造成"幻觉"。
- 丢弃修改:两个写事务T1 T2同时对A=0进行递增操作,结果T2覆盖T1,导致最终结果是1 而不是2,事务被覆盖
- 不可重复读:当设置T2事务只能读取 T1 事务已经提交的部分,T2 读取一个数据,然后T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。
四种隔离级别
- 未提交读(READ UNCOMMITTED):事务中发生了修改,即使没有提交,其他事务也是可见的,比如对于一个数A原来50修改为100,但是我还没有提交修改,另一个事务看到这个修改,而这个时候原事务发生了回滚,这时候A还是50,但是另一个事务看到的A是100.可能会导致脏读、幻读或不可重复读
- 已提交读(READ COMMITTED):对于一个事务从开始直到提交之前,所做的任何修改是其他事务不可见的,举例就是对于一个数A原来是50,然后提交修改成100,这个时候另一个事务在A提交修改之前,读取的A是50,刚读取完,A就被修改成100,这个时候另一个事务再进行读取发现A就突然变成100了;可以阻止脏读,但是幻读或不可重复读仍有可能发生
- 可重复读(REPEATABLE READ):就是对一个记录读取多次的记录是相同的,比如对于一个数A读取的话一直是A,前后两次读取的A是一致的;可以阻止脏读和不可重复读,但幻读仍有可能发生
- 可串行化(SERIALIZABLE):在并发情况下,和串行化的读取的结果是一致的,没有什么不同,比如不会发生脏读和幻读;该级别可以防止脏读、不可重复读以及幻读
不可重复读的重点是修改,幻读的重点在于新增或者删除。
innodb默认支持repeatable-read(可重复读)加锁算法可以避免幻读产生
锁分类:
-
按锁粒度分:
- 行锁:
- 描述:mysql中锁定粒度最细的一种锁。表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突,其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁和排他锁
- 特点:开销大,加锁慢,会出现死锁。发生锁冲突的概率最低,并发度也最高。
- 表锁
- 描述:mysql中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分mysql引擎支持。最常使用的MyISAM与InnoDB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)
- 特点: 开销小,加锁快,不会出现死锁。发生锁冲突的概率最高,并发度也最低。
- 页锁
- 描述: MySQL 中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。因此,采取了折衷的页级锁,一次锁定相邻的一组记录。BDB 支持页级锁。
- 特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
- 行锁:
-
按使用方式分:
- 共享锁
- 描述:共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。
如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获取共享锁的事务只能读数据,不能修改数据。 - 用法:SELECT … LOCK IN SHARE MODE;
在查询语句后面增加LOCK IN SHARE MODE,MySQL 就会对查询结果中的每行都加共享锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。其他线程也可以读取使用了共享锁的表,而且这些线程读取的是同一个版本的数据。
- 描述:共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。
- 排它锁
- 描述:排他锁又称写锁、独占锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任何类型的封锁。获取排他锁的事务既能读数据,又能修改数据。
- 用法:SELECT … FOR UPDATE;
在查询语句后面增加FOR UPDATE,MySQL 就会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。
- 共享锁
-
按思想分:
- 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。 乐观锁不能解决脏读的问题。每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
- 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
- 场景:
- 悲观锁,先获取锁,再进行业务操作,一般就是利用类似 SELECT … FOR UPDATE 这样的语句,对数据加锁,避免其他事务意外修改数据。当数据库执行SELECT … FOR UPDATE时会获取被select中的数据行的行锁,select for update获取的行锁会在当前事务结束时自动释放,因此必须在事务中使用。
- 乐观锁,先进行业务操作,只在最后实际更新数据时进行检查数据是否被更新过。Java 并发包中的 AtomicFieldUpdater 类似,也是利用 CAS 机制,并不会对数据加锁,而是通过对比数据的时间戳或者版本号,来实现乐观锁需要的版本判断。
用存储引擎的锁机制
- MyISAM和MEMORY采用表级锁(table-level locking)
- BDB采用页面锁(page-level locking)或表级锁,默认为页面锁
- InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁
存储引擎有几种锁算法
- Record Lock — 单个行记录上的锁;
- Gap Lock — 间隙锁,锁定一个范围,不包括记录本身;
- Next-Key Lock — 锁定一个范围,包括记录本身。
死锁
- 是指二个或者二个以上的进程在执行时候,因为争夺资源造成相互等待的现象,进程一直处于等待中,无法得到释放,这种状态就叫做死锁。
- 案例:批量入库,存在则更新,不存在则插入,
insert into tab(xx,xx) on duplicate key update xx=‘xx’
。 - 处理:
- 通过
innodblockwait_timeout
来设置超时时间,一直等待直到超时 - 发起死锁检测,发现死锁之后,主动回滚死锁中的事务,不需要其他事务继续
- 通过
- 避免
- 为了在单个innodb表上执行多个并发写入操作时避免死锁,可以在事务开始时,通过为预期要修改行,使用select …for update语句来获取必要的锁,即使这些行的更改语句是在之后才执行的
- 在事务中,如果要更新记录,应该直接申请足够级别的锁,即排他锁,而不应先申请共享锁,更新时在申请排他锁。因为这时候当用户在申请排他锁时,其他事务可能又已经获得了相同记录的共享锁
- 如果事务需要修改或锁定多个表,则应在每个事务中以相同的顺序使用加锁语句。在应用中,如果不同的程序会并发获取多个表,应尽量约定以相同的顺序来访问表,这样可以大大降低产生死锁的机会
- 通过 select …lock in share mode获取行的读锁后,如果当前事务在需要对该记录进行更新操作,则很有可能造成死锁
- 改变事务隔离级别
- innodb处理死锁:设置死锁时间来让死锁超时的策略,默认
innodblockwait_timeout
设置的时长是50s - 开启死锁检测:设置
innodbdeadlockdetect
设置为on可以主动检测死锁,在innodb中这个值默认就是on开启的状态 - 全局锁,应用场景
- 对整个数据库实例加锁
- 做全库逻辑备份,这个命令可以使用整个库处于只读状态,使用该命令之后,数据更新语句,数据定义语句,更新类事务的提交语句等操作都会被阻塞。
- 导致的问题
- 如果在主库备份,在备份期间不能更新,业务停止,所以更新业务会处于等待状态
- 如果在从库备份,在备份期间不能执行主库同步的binlog,导致主从延迟
- 优化锁
- 尽量使用较低的隔离级别。
- 精心设计索引, 并尽量使用索引访问数据, 使加锁更精确, 从而减少锁冲突的机会。
- 选择合理的事务大小,小事务发生锁冲突的几率也更小。
- 给记录集显示加锁时,最好一次性请求足够级别的锁。比如要修改数据的话,最好直接申请排他锁,而不是先申请共享锁,修改时再请求排他锁,这样容易产生死锁。
- 不同的程序访问一组表时,应尽量约定以相同的顺序访问各表,对一个表而言,尽可能以固定的顺序存取表中的行。这样可以大大减少死锁的机会。
- 尽量用相等条件访问数据,这样可以避免间隙锁对并发插入的影响。
- 不要申请超过实际需要的锁级别。
- 除非必须,查询时不要显示加锁。 MySQL 的 MVCC 可以实现事务中的查询不用加锁,优化事务性能;MVCC 只在 COMMITTED READ(读提交)和 REPEATABLE READ(可重复读)两种隔离级别下工作。
- 对于一些特定的事务,可以使用表锁来提高处理速度或减少死锁的可能。
mysql内部构造
- 服务层:连接器,查询缓存,分析器,优化器,执行器等
- 存储引擎:负责数据的存储和提取
server层按顺序执行sql步骤:
1.客户端请求->
2.连接器(验证用户身份,给予权限) ->
3.查询缓存(存在缓存则直接返回,不存在则执行后续操作)->
4.分析器(对SQL进行词法分析和语法分析操作) ->
5.优化器(主要对执行的sql优化选择最优的执行方案方法) ->
6.执行器(执行时会先看用户是否有执行权限,有才去使用这个引擎提供的接口)->
7.去引擎层获取数据返回(如果开启查询缓存则会缓存查询结果)
表结构相关
Drop、Delete与Truncate的共同点和区别
- Drop直接删掉表;
- Truncate删除表中数据,再插入时自增长id又从1开始 ;
- Delete删除表中数据,可以加where字句。
MySQL数据库cpu飙升的话,要怎么处理
排查过程:
- 使用top 命令观察,确定是mysqld导致还是其他原因。
- 如果是mysqld导致的,show processlist,查看session情况,确定是不是有消耗资源的sql在运行。
- 找出消耗高的 sql,看看执行计划是否准确, 索引是否缺失,数据量是否太大。
处理
-
kill 掉这些线程(同时观察 cpu 使用率是否下降)
-
进行相应的调整(比如说加索引、改 sql、改内存参数)
-
重新跑这些 SQL。
mysql主从延迟,怎么解决
主从复制五个步骤
- 步骤一:主库的更新事件(update、insert、delete)被写到binlog
- 步骤二:从库发起连接,连接到主库。
- 步骤三:此时主库创建一个binlog dump thread,把binlog的内容发送到从库。
- 步骤四:从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log
- 步骤五:还会创建一个SQL线程,从relay log里面读取内容,从Exec_Master_Log_Pos位置开始执行读取到的更新事件,将更新内容写入到slave的db
主从同步延迟的原因:
一个服务器开放N个链接给客户端来连接的,会有大并发的更新操作, 但从服务器的里面读取binlog的线程仅有一个,当某个SQL在从服务器上执行的时间稍长 或者由于某个SQL要进行锁表就会导致,主服务器的SQL大量积压,未被同步到从服务器里。这就导致了主从不一致, 也就是主从延迟。
主从同步延迟的解决办法:
主服务器要负责更新操作,对安全性的要求比从服务器要高,所以有些设置参数可以修改,比如sync_binlog=1,innodb_flush_log_at_trx_commit = 1 之类的设置等。
选择更好的硬件设备作为slave。
把一台从服务器当度作为备份使用, 而不提供查询, 那边他的负载下来了, 执行relay log 里面的SQL效率自然就高了。
增加从服务器,这个目的还是分散读的压力,从而降低服务器负载。
分库分表
-
水平分库:以字段为依据,按照一定策略(hash、range等),将一个库中的数据拆分到多个库中。
-
水平分表:以字段为依据,按照一定策略(hash、range等),将一个表中的数据拆分到多个表中。
-
垂直分库:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中。
-
垂直分表:以字段为依据,按照字段的活跃性,将表中字段拆到不同的表(主表和扩展表)中。
分库分表中间件:sharding-jdbc、mycat
分库分表的问题:
-
事务问题:需要用分布式事务
-
跨节点Join的问题:解决这一问题可以分两次查询实现
-
跨节点的count,order by,group by以及聚合函数问题:分别在各个节点上得到结果后在应用程序端进行合并。
-
数据迁移,容量规划,扩容等问题
-
ID问题:数据库被切分后,不能再依赖数据库自身的主键生成机制,最简单可以考虑UUID
-
跨分片的排序分页问题
count(1)、count(*)、count(列名)
- count(*):包括了所有的列,相当于行数,在统计结果的时候, 不会忽略列值为NULL
- count(1):包括了忽略所有列,用1代表代码行,在统计结果的时候, 不会忽略列值为NULL
- count(列名):只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者0,而是表示null)的计数, 即某个字段值为NULL时,不统计。
执行效率上:
-
列名为主键,count(列名)会比count(1)快
-
列名不为主键,count(1)会比count(列名)快
-
如果表多个列并且没有主键,则 count(1) 的执行效率优于 count()
-
如果有主键,则 select count(主键)的执行效率是最优的
-
如果表只有一个字段,则 select count()最优。
sql中null与空值的区别
- 占用空间区别:空值(’’)的长度是0,是不占用空间的;而NULL的长度是NULL,是占用空间的
- 插入/查询方式区别:NULL值查询使用is null/is not null查询,而空值(’’)可以使用=或者!=、<、>等算术运算符。
- COUNT 和 IFNULL函数:使用 COUNT(字段) 统计会过滤掉 NULL 值,但是不会过滤掉空值。
- 索引字段说明:在有NULL值的字段上使用常用的索引,如普通索引、复合索引、全文索引等不会使索引失效。在官网查看在空间索引的情况下,说明了 索引列必须为NOT NULL。
优化
表结构优化
- 尽量使用数字型字段,数值信息的字段不设计为字符型
- 尽可能用varchar代替char:变长字段存储空间小,节省存储空间
- 当索引列大量重复数据时,可以把索引删除掉(eg:性别字段索引无效)
查询优化
- 应尽量避免在 where 子句中使用!=或<>操作符
- 应尽量避免在 where 子句中使用 or 来连接条件
- 任何查询也不要出现select *
- 避免在 where 子句中对字段进行 null 值判断
索引优化
- 对作为查询条件和 order by的字段建立索引
- 避免建立过多的索引,多使用组合索引
慢查询优化
- 分析语句,是否加载了不必要的字段/数据
- 分析 SQL 执行句话,是否命中索引等
- 如果 SQL 很复杂,优化 SQL 结构
- 如果表数据量太大,考虑分表