当数据库有并发事务的时候,保证数据访问顺序的机制称为锁机制。
数据库的锁与隔离级别的关系?
Mysql锁类别 | 资源开销 | 加锁速度 | 是否会出现死锁 | 锁的粒度 | 并发度 |
表级锁 | 小 | 快 | 不会 | 大 | 低 |
行级锁 | 大 | 慢 | 会 | 小 | 高 |
页面锁 | 一般 | 一般 | 不会 | 一般 | 一般 |
MyISAM 默认采用表级锁,InnoDB 默认采用行级锁。
从锁的类别上区别可以分为共享锁和排他锁
共享锁:共享锁又称读锁,简写为S锁,一个事务对一个数据对象加了S锁,可以对这个数据对象进行读取操作,但不能进行更新操作。并且在加锁期间其他事务只能对这个数据对象加S锁,不能加X锁。
排他锁:排他锁又称为写锁,简写为X锁,一个事务对一个数据对象加了X锁,可以对这个对象进行读取和更新操作,加锁期间,其他事务不能对该数据对象进行加X锁或S锁。
乐观锁:系统假设数据的更新在大多数时候是不会产生冲突的,所以数据库只在更新操作提交的时候对数据检测冲突,如果存在冲突,则数据更新失败。
乐观锁实现方式:一般通过版本号和CAS算法实现。
悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。通俗讲就是每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。
悲观锁的实现方式:通过数据库的锁机制实现,对查询语句添加for updata。
死锁是指两个或者两个以上进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象。在 MySQL 中,MyISAM 是一次获得所需的全部锁,要么全部满足,要么等待,所以不会出现死锁。在 InnoDB 存储引擎中,除了单个 SQL 组成的事务外,锁都是逐步获得的,所以存在死锁问题。
如何避免MySQL发生死锁或锁冲突:
如果不同的程序并发存取多个表,尽量以相同的顺序访问表。
在程序以批量方式处理数据的时候,如果已经对数据排序,尽量保证每个线程按照固定的顺序来处理记录。
在事务中,如果需要更新记录,应直接申请足够级别的排他锁,而不应该先申请共享锁,更新时在申请排他锁,因为在当前用户申请排他锁时,其他事务可能已经获得了相同记录的共享锁,从而造成锁冲突或者死锁。
尽量使用较低的隔离级别。2
尽量使用索引访问数据,使加锁更加准确,从而减少锁冲突的机会。
合理选择事务的大小,小事务发生锁冲突的概率更低。
尽量用相等的条件访问数据,可以避免Next-Key锁对并发插入的影响。
不要申请超过实际需要的锁级别,查询时尽量不要显示加锁。
对于一些特定的事务,可以表锁来提高处理速度或减少死锁的概率。
B 树适用于随机检索,而 B+ 树适用于随机检索和顺序检索。
B+ 树的空间利用率更高,因为 B 树每个节点要存储键和值,而 B+ 树的内部节点只存储键,这样 B+ 树的一个节点就可以存储更多的索引,从而使树的高度变低,减少了 I/O 次数,使得数据检索速度更快。
B+ 树的叶子节点都是连接在一起的,所以范围查找,顺序查找更加方便。
B+ 树的性能更加稳定,因为在 B+ 树中,每次查询都是从根节点到叶子节点,而在 B 树中,要查询的值可能不在叶子节点,在内部节点就已经找到。
因为 B 树的内部节点也可以存储值,所以可以把一些频繁访问的值放在距离根节点比较近的地方,这样就可以提高查询效率。
1.通过连接器跟客户端「建立连接」。
2.通过查询「缓存查询」之前是否有查询过该 sql。
有则直接返回结果
没有则执行第3步
3.通过分析器「分析该 sql 的语义」是否正确,包括格式,表等等。
4.通过优化器「优化该语句」,比如选择索引,join 表的连接顺序。
5.「验证权限」,验证是否有该表的查询权限。
没有则返回无权限的错误
有则执行第6步
6.通过执行器调用存储引擎执行该 sql,然后返回「执行结果」。
1.binlog 是归档日志,属于 Server 层的日志,是一个二进制格式的文件,用于「记录用户对数据库更新的SQL语句信息」。
主要作用:主从复制、数据恢复。
2.undolog 是 InnoDB 存储引擎的日志,用于保证数据的原子性,「保存了事务发生之前的数据的一个版本,也就是说记录的是数据是修改之前的数据,可以用于回滚」,同时可以提供多版本并发控制下的读(MVCC)。
主要作用:事务回滚、实现多版本控制(MVCC)。
3.relaylog 是中继日志,「在主从同步的时候使用到」,它是一个中介临时的日志文件,用于存储从master节点同步过来的binlog日志内容。
4.redolog 是 「InnoDB 存储引擎所特有的一种日志」,用于记录事务操作的变化,记录的是数据修改之后的值,不管事务是否提交都会记录下来。
可以做「数据恢复并且提供 crash-safe 能力」。当有增删改相关的操作时,会先记录到 Innodb 中,并修改缓存页中的数据,「等到 mysql 闲下来的时候才会真正的将 redolog 中的数据写入到磁盘当中」。
两阶段提交分为 prepare 和 commit 阶段:
准备阶段:事物 SQL 先写入 redo log buffer,然后做一个事物准备标记,在将log buffer 中的数据刷新到 redo log。
提交阶段:将事物产生的 binlog 写入文件,刷新磁盘。
2.再在 redo log 中做一个事物提交的标记,并把 binlog 写成功的标记也一并写入 redo log 文件。
场景分析两阶段提交如何保证数据库的一致性。
准备阶段,redo log 刷新到磁盘了,但是 binlog 写磁盘前发生了 mysql实例 crash,这时会发生怎样的操作呢?
即使 redo log 写盘成功了,但由于 binlog 未写入成功,需要执行回滚操作来保证数据库的一致性。
2.提交阶段,binlog 写盘成功了,这时 mysql 实例 crash了。这时 binlog 已经确保写成功了,我们在重启实例进行恢复的时候,只需要让 redo log 重做一次就可以了。
水平分库:以字段为依据,按照一定策略(hash、range等),将一个库中的数据拆分到多个库中。水平分表:以字段为依据,按照一定策略(hash、range等),将一个表中的数据拆分到多个表中。垂直分库:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中。垂直分表:以字段为依据,按照字段的活跃性,将表中字段拆到不同的表(主表和扩展表)中。
sharding-jdbc
Mycat
事务问题:需要用分布式事务。
跨节点Join的问题:解决这一问题可以分两次查询实现。
跨节点的count,order by,group by以及聚合函数问题:分别在各个节点上得到结果后在应用程序端进行合并。
数据迁移,容量规划,扩容等问题。
ID问题:数据库被切分后,不能再依赖数据库自身的主键生成机制啦,最简单可以考虑UUID。
跨分片的排序分页问题。
专库专用 一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分布到不同的数据库上面,这样也就将数据或者说压力分担到不同的库上面
优点:
拆分后业务清晰,拆分规则明确。
系统之间整合或扩展容易。
数据维护简单。
缺点:
部分业务表无法join,只能通过接口方式解决,提高了系统复杂度。
受每种业务不同的限制存在单库性能瓶颈,不易数据扩展跟性能提高。
事务处理复杂。
垂直拆分后遇到单机瓶颈,可以使用水平拆分。相对于垂直拆分的区别是:垂直拆分是把不同的表拆到不同的数据库中,而水平拆分是把同一个表拆到不同的数据库中。
相对于垂直拆分,水平拆分不是将表的数据做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中 的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中,主要有分表,分库两种模式,
优点:
不存在单库大数据,高并发的性能瓶颈。
对应用透明,应用端改造较少。
按照合理拆分规则拆分,join操作基本避免跨库。
提高了系统的稳定性跟负载能力。
缺点:
拆分规则难以抽象。
分片事务一致性难以解决。
数据多次扩展难度跟维护量极大。
跨库join性能较差。
1.master 主库将此次更新的事件类型「写入到主库的 binlog 文件」中。
2.master 「创建 log dump 线程通知 slave」 需要更新数据。
3.「slave」 向 master 节点发送请求,「将该 binlog 文件内容存到本地的 relaylog 中」。
4.「slave 开启 sql 线程」读取 relaylog 中的内容,「将其中的内容在本地重新执行一遍」,完成主从数据同步。
1.「全同步复制」:主库强制同步日志到从库,等全部从库执行完才返回客户端,性能差。2.「半同步复制」:主库收到至少一个从库确认就认为操作成功,从库写入日志成功返回ack确认。
主库的更新事件(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大量积压,未被同步到从服务器里。这就导致了主从不一致, 也就是主从延迟。
1.MySQL 5.6 版本以后,提供了一种「并行复制」的方式,通过将 SQL 线程转换为多个 work 线程来进行重放。2.「提高机器配置」增加从服务器,目的分散读的压力,从而降低服务器负载。3.在业务初期就选择合适的分库、分表策略,「避免单表单库过大」带来额外的复制压力 4.「避免长事务」。5.「避免让数据库进行各种大量运算」。6.对于一些对延迟很敏感的业务「直接使用主库读」。
「表结构优化」
拆分字段
字段类型的选择
字段类型大小的限制
合理的增加冗余字段
新建字段一定要有默认值
「索引方面」
索引字段的选择
利用好mysql支持的索引下推,覆盖索引等功能
唯一索引和普通索引的选择
「查询语句方面」
避免索引失效
合理的书写where条件字段顺序
小表驱动大表
可以使用force index()防止优化器选错索引
「分库分表」
慢查询一般用于记录执行时间超过某个临界值的SQL语句的日志。
相关参数:
slow_query_log:是否开启慢日志查询,1表示开启,0表示关闭。slow_query_log_file:MySQL数据库慢查询日志存储路径。long_query_time:慢查询阈值,当SQL语句查询时间大于阈值,会被记录在日志上。log_queries_not_using_indexes:未使用索引的查询会被记录到慢查询日志中。log_output:日志存储方式。“FILE”表示将日志存入文件。“TABLE”表示将日志存入数据库。如何对慢查询进行优化?
分析语句的执行计划,查看SQL语句的索引是否命中 优化数据库的结构,将字段很多的表分解成多个表,或者考虑建立中间表。优化LIMIT分页。
首先说一点,NULL和空值是不一样的,空值是不占用空间的,而NULL是占用空间的,所以字段设为NOT NULL后仍然可以插入空值。
字段设置成not null主要有以下几点原因:
NULL值会影响一些函数的统计,如count,遇到NULL值,这条记录不会统计在内。
B树不存储NULL,所以索引用不到NULL,会造成第一点中说的统计不到的问题。
NOT IN子查询在有NULL值的情况下返回的结果都是空值。
varchar表示变长,char表示长度固定。
存储容量不同,对于 char 来说,最多能存放的字符个数为255。对于 varchar,最多能存放的字符个数是 65532。
存储速度不同,char 长度固定,存储速度会比 varchar 快一些,但在空间上会占用额外的空间,属于一种空间换时间的策略。而 varchar 空间利用率会高些,但存储速度慢,属于一种时间换空间的策略。
当定义varchar长度小于等于255时,长度标识位需要一个字节(utf-8编码)。
当大于255时,长度标识位需要两个字节,并且建立的索引也会失效。
varchar 的10代表了申请的空间长度,也是可以存储的数据的最大长度。
int 的10只是代表了展示的长度,不足10位以0填充。
int(1)和int(10)所能存储的数字大小以及占用的空间都是相同的,只是在展示时按照长度展示。
drop | delete | truncate | |
速度 | 快 | 逐行删除,慢 | 较快 |
类型 | DDL | DML | DDL |
回滚 | 不可回滚 | 可回滚 | 不可回滚 |
删除内容 | 删除整个表,数据行、索引都会被删除 | 表结构还在,删除表的一部分或全部数据 | 表结构还在,删除表的全部数据 |
总结:删除整个表,使用drop,删除表的部分数据使用delete,保留表结构删除表的全部数据使用truncate。
慢查询一般用于记录执行时间超过某个临界值的SQL语句的日志。
记录慢查询日志,分析查询日志,可以使用pt-query-digest工具进行分析。
相关参数:
slow_query_log:是否开启慢日志查询,1表示开启,0表示关闭。
slow_query_log_file:MySQL数据库慢查询日志存储路径。
long_query_time:慢查询阈值,当SQL语句查询时间大于阈值,会被记录在日志上。
log_queries_not_using_indexes:未使用索引的查询会被记录到慢查询日志中。
log_output:日志存储方式。“FILE”表示将日志存入文件。“TABLE”表示将日志存入数据库。
2.show profile
3.show status
show status 会返回一些计数器,show global status 会查看所有服务器级别的所有计数。有时根据这些计数,可以推测出哪些操作代价较高或者消耗时间多。
4.show processlist
观察是否有大量线程处于不正常的状态或特征
5.使用 explain 分析语句
分析慢语句是否命中索引
分析语句的执行计划,查看SQL语句的索引是否命中。
优化数据库的结构,将字段很多的表分解成多个表,或者考虑建立中间表。
优化LIMIT分页。
图片及部分相关技术知识点来源于网络搜索,侵权删!