天津网站策划,建设企业网站需要使用哪些技术,wordpress插件管理本地资源,大连做网站建设Mysql 文章目录 Mysql关系型数据库和非关系型数据库的区别什么是ORM?-**mybatis**如何评估一个索引创建的是否合理#xff1f;Count函数执行效果上#xff1a;执行效率上#xff1a;count(主键)和count(列名) 数据库的三大范式Mysql中char和varchar的区别数据库设计或者功能…Mysql 文章目录 Mysql关系型数据库和非关系型数据库的区别什么是ORM?-**mybatis**如何评估一个索引创建的是否合理Count函数执行效果上执行效率上count(主键)和count(列名) 数据库的三大范式Mysql中char和varchar的区别数据库设计或者功能开发中要考虑到什么细节数据库创建表语句如何评估一个索引创建的是否合理Mysql索引的分类非聚集索引的分类请你说说MySQL索引,以及它们的好处和坏处索引的实现原理Mysql的底层结构请你说说MySQL索引,以及它们的好处和坏处sql语句如何指定索引Mysql中char和varchar的区别索引的实现原理sql语句学生成绩表姓名科目成绩查询每门课成绩都80分的学生姓名。添加索引 SQL注入如何阻止 数据库SQL调优的几种方式SQL语句的执行顺序如何优化SQL查询Mysql删除数据TRUNCATE 和 DELETE 的区别 MySQL如何优化?explain关注什么表中有几千万条数据怎么办MySQL的慢查询优化有了解吗关系型和非关系型数据库的区别你了解多少Mysql查询语句加锁最左匹配原则缓存与数据库的一致性如何保证第二步成功主从库延迟和延迟双删问题总结 使用先更新数据库在删除缓存的操作cannel消息队列Mysql的琐机制乐观锁和悲观锁 数据冗余Mysql宕机之后怎么办用哪个日志恢复具体怎么恢复的知道吗两阶段提交两段提交两段提交详细解释如果不小心删除了一个表你知道用什么恢复吗是什么是事务?事务的使用场景事务的隔离级别MVCCACID和锁间隙锁MVCCMVCC➕Next-key-Lock 防止幻读什么时候会使用到间隙锁说一说你对redo log、undo log、binlog的了解数据库如何保证一致性数据库如何保证原子性数据库如何保证持久性数据库的左链接和右链接Mysql查询语句加锁是什么是事务?事务的使用场景Mysql常用的数据类型数值类型日期类型字符串类型 大表优化读写分离缓存垂直拆分水平拆分 主从复制的原理项目中如何解决高并发redo log日志为什么要有redolog日志直接刷盘不行吗varchar和char的区别varchar(255)存多少个汉字MySQL的一级缓存和二级缓存Mybatis的一级缓存和二级缓存了解MyBatis缓存机制吗 Mybatis的一级缓存和二级缓存了解MyBatis缓存机制吗 关系型数据库和非关系型数据库的区别
关系型数据库的优点 容易理解。因为它采用了关系模型来组织数据。可以保持数据的一致性。数据更新的开销比较小。支持复杂查询带where子句的查询 非关系型数据库的优点 不需要经过SQL层的解析读写效率高。基于键值对数据的扩展性很好。可以支持多种类型数据的存储如图片文档等等。
什么是ORM?-mybatis
对象关系映射Object Relational Mapping简称ORM模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。ORM框架是连接数据库的桥梁只要提供了持久化类与表的映射关系ORM框架在运行时就能参照映射文件的信息把对象持久化到数据库中。
优点 1提高开发效率降低开发成本2使开发更加对象化3可移植4可以很方便地引入数据缓存之类的附加功能 缺点 1自动化进行关系数据库的映射需要消耗系统性能。其实这里的性能消耗还好啦一般来说都可以忽略之。2在处理多表联查、where条件复杂之类的查询时ORM的语法会变得复杂。
如何评估一个索引创建的是否合理
建议按照如下的原则来设计索引
避免对经常更新的表进行过多的索引并且索引中的列要尽可能少。应该经常用于查询的字段创建索引但要避免添加不必要的字段。数据量小的表最好不要使用索引由于数据较少查询花费的时间可能比遍历索引的时间还要短索引可能不会产生优化效果。在条件表达式中经常用到的不同值较多的列上建立索引在不同值很少的列上不要建立索引。比如在学生表的“性别”字段上只有“男”与“女”两个不同值因此就无须建立索引如果建立索引不但不会提高查询效率反而会严重降低数据更新速度。当唯一性是某种数据本身的特征时指定唯一索引。使用唯一索引需能确保定义的列的数据完整性以提高查询速度。在频繁进行排序或分组即进行group by或order by操作的列上建立索引如果待排序的列有多个可以在这些列上建立组合索引。
Count函数
执行效果上
count(*)包括了所有的列相当于行数在统计结果的时候不会忽略列值为NULL count(1)包括了忽略所有列用1代表代码行在统计结果的时候不会忽略列值为NULL count(列名)只包括列名那一列在统计结果的时候会忽略列值为空这里的空不是只空字符串或者0而是表示null的计数即某个字段值为NULL时不统计。
执行效率上
列名为主键count(列名)会比count(1)快 列名不为主键count(1)会比count(列名)快 如果表多个列并且没有主键则 count1 的执行效率优于 count* 如果有主键则 select count主键的执行效率是最优的 如果表只有一个字段则 select count *最优。 count(可空字段) count(非空字段) count(主键 id) count(1) ≈ count(*) count(主键)和count(列名)
主键不为null如果列名当中有null值那么他们的返回是不一样的
数据库的三大范式
第一范式1NF要求数据库表的每一列都是不可分割的原子数据项。
第二范式2NF在 1NF 的基础上有主键非主键字段依赖于主键字段要求数据库表中的每个实例或记录必须可以被唯一地区分
第三范式3NF在 2NF 基础上任何非主属性不依赖于其它非主属性在 2NF 基础上消除传递依赖 第三范式需要确保数据表中的每一列数据都和主键直接相关而不能间接相关。
Mysql中char和varchar的区别
char 是固定长度的varchar 是可变长度的
char 如果某个长度小于MMySQL就会在它的右边用空格补足使长度达到M
varchar 每个值只占用刚好够用的字节再加上一个用来记录其长度的字节当长度小于255时长度记录位占一个字节大于时占用两个字节
数据库设计或者功能开发中要考虑到什么细节
数据库和表的字符集统一使用 UTF8所有表和字段都需要添加注释尽量做到冷热数据分离,减小表的宽度尽可能把所有列定义为 NOT NUL禁止在数据库中存储图片,文件等大的二进制数据优先选择符合存储需要的最小的数据类型避免使用 TEXT,BLOB 数据类型最常见的 TEXT 类型可以存储 64k 的数据可以将text数据单独成表
数据库创建表语句
CREAT TABLE table_name{ID INT UNSIGNED AUTO_INCREMENT,字段1 varchar(100) NOT NULL,
}ENGINE InnoDB DEFUALT CHAREST utf-8如何评估一个索引创建的是否合理
建议按照如下的原则来设计索引
避免对经常更新的表进行过多的索引并且索引中的列要尽可能少。应该经常用于查询的字段创建索引但要避免添加不必要的字段。数据量小的表最好不要使用索引由于数据较少查询花费的时间可能比遍历索引的时间还要短索引可能不会产生优化效果。在条件表达式中经常用到的不同值较多的列上建立索引在不同值很少的列上不要建立索引。比如在学生表的“性别”字段上只有“男”与“女”两个不同值因此就无须建立索引如果建立索引不但不会提高查询效率反而会严重降低数据更新速度。当唯一性是某种数据本身的特征时指定唯一索引。使用唯一索引需能确保定义的列的数据完整性以提高查询速度。在频繁进行排序或分组即进行group by或order by操作的列上建立索引如果待排序的列有多个可以在这些列上建立组合索引。
Mysql索引的分类
主键索引、唯一索引、联合索引、普通索引、全文索引、空间索引
非聚集索引的分类
辅助索引、MyiSam的索引
请你说说MySQL索引,以及它们的好处和坏处
索引是一个单独的、存储在磁盘上的数据结构包含着对数据表里所用记录的引用指针使用索引可以提高查询效率索引是在存储引擎中实现的因此每一种存储引擎的索引都不完全相同。常见的索引结果有Hash和Btree。
索引的好处
提高查询速度在使用分组和排序子句进行数据查询时可以显著减少查询中分组和排序的时间通过创建唯一索引可以保证数据库表中的每一行数据的唯一性
缺点
创建索引和维护索引是需要浪费时间的随着数据量的增加耗费的时间也会随之增加索引需要占用磁盘空间当对表进行插入删除和修改等操作时索引也需要动态的维护降低了数据维护的速度
索引的实现原理
BTree树索引和Hash索引
BTree树的非叶子节点只存储了key信息这样的话可以才存储更多的索引key降低了BTree树的高度叶子节点存放key和value值每一次查询都要从根节点进行查询到叶子节点这样每一次查询的效率都是稳定的同时叶子节点有指向相邻节点的指针提高了范围查找的效率
Hash索引是根据字段的哈希值建立一个hash表key 是哈希值value 是该数据对应的地址
Mysql的底层结构
服务层连接器、查询缓存、分析器、优化器、执行器
存储引擎负责数据的存储和提取
请你说说MySQL索引,以及它们的好处和坏处
索引是一个单独的、存储在磁盘上的数据结构包含着对数据表里所用记录的引用指针使用索引可以提高查询效率索引是在存储引擎中实现的因此每一种存储引擎的索引都不完全相同。常见的索引结果有Hash和Btree。
索引的好处
提高查询速度在使用分组和排序子句进行数据查询时可以显著减少查询中分组和排序的时间通过创建唯一索引可以保证数据库表中的每一行数据的唯一性
缺点
创建索引和维护索引是需要浪费时间的随着数据量的增加耗费的时间也会随之增加索引需要占用磁盘空间当对表进行插入删除和修改等操作时索引也需要动态的维护降低了数据维护的速度
sql语句如何指定索引
SELECT 字段名表
FROM 表名表
WITH (INDEX(索引名))
WHERE 查询条件Mysql中char和varchar的区别
char 是固定长度的varchar 是可变长度的
char 如果某个长度小于MMySQL就会在它的右边用空格补足使长度达到M
varchar 每个值只占用刚好够用的字节再加上一个用来记录其长度的字节当长度小于255时长度记录位占一个字节大于时占用两个字节
索引的实现原理
BTree树索引和Hash索引
BTree树的非叶子节点只存储了key信息这样的话可以才存储更多的索引key降低了BTree树的高度叶子节点存放key和value值每一次查询都要从根节点进行查询到叶子节点这样每一次查询的效率都是稳定的同时叶子节点有指向相邻节点的指针提高了范围查找的效率
Hash索引是根据字段的哈希值建立一个hash表key 是哈希值value 是该数据对应的地址
sql语句
学生成绩表姓名科目成绩查询每门课成绩都80分的学生姓名。
select name from table group by name having min(score) 80;
select name from table group by name having count(score) sum(case when score 80 then 1 else 0 end)添加索引
ALTER TABLE table_name ADD PRIMARY KEY ( column )//主键索引
ALTER TABLE table_name ADD UNIQUE ( column )//唯一索引
ALTER TABLE table_name ADD INDEX index_name ( column )
ALTER TABLE table_name ADD INDEX index_name ( column1, column2, column3 )//联合索引
ALTER TABLE 表名 ADD INDEX (字段);SQL注入
SQL注入即是指 web应用程序 对用户输入数据的合法性没有判断或过滤不严攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的 SQL语句 在管理员不知情的情况下实现非法操作以此来实现欺骗数据库服务器执行非授权的任意查询从而进一步得到相应的数据信息
如何阻止
(1) 过滤用户输入参数中的特殊字符从而降低被 SQL 注入入的风险。
(2) 禁止通过字符拼接的 SQL 语句严恪使用参数绑定传入的 SQL 参数。
(3) 合理使用数据库访问框架提供的防注入机制。比如 MyBatis 提供的 绑定参数从而防止 SQL注入。同时谨慎使用${ } , $ 相当于使用字符拼接 SQL。
数据库SQL调优的几种方式
(6条消息) 数据库SQL调优的几种方式_lss0555的博客-CSDN博客_sql调优 创建索引 要尽量避免全表扫描首先应考虑在 where 及 order by 涉及的列上建立索引在经常需要进行检索的字段上创建索引比如要按照表字段username进行检索那么就应该在姓名字段上创建索引如果经常要按照员工部门和员工岗位级别进行检索那么就应该在员工部门和员工岗位级别这两个字段上创建索引。 创建索引给检索带来的性能提升往往是巨大的因此在发现检索速度过慢的时候应该首先想到的就是创建索引。一个表的索引数最好不要超过6个若太多则应考虑一些不常使用到的列上建的索引是否有 必要。索引并不是越多越好索引固然可以提高相应的 select 的效率但同时也降低了 insert 及 update 的效率因为 insert 或 update 时有可能会重建索引所以怎样建索引需要慎重考虑视具体情况而定。 避免在索引列上做计算 使用预编译查询 调整Where字句中的连接顺序 根据这个原理表连接最好写在其他where条件之前那些可以过滤掉最大数量记录。 尽量将多条SQL语句压缩到一句SQL中 每次执行SQL的时候都要建立网络连接、进行权限校验、进行SQL语句的查询优化、发送执行结果这个过程 是非常耗时的因此应该尽量避免过多的执行SQL语句能够压缩到一句SQL执行的语句就不要用多条来执行。 用where字句替换HAVING字句 避免使用HAVING字句因为HAVING只会在检索出所有记录之后才对结果集进行过滤而where则是在聚合前刷选记录如果能通过where字句限制记录的数目那就能减少这方面的开销。HAVING中的条件一般用于聚合函数的过滤除此之外应该将条件写在where字句中 使用表的别名 当在SQL语句中连接多个表时请使用表的别名并把别名前缀于每个列名上。这样就可以减少解析的时间并减 少哪些友列名歧义引起的语法错误。 用union all替换union 当SQL语句需要union两个查询结果集合时即使检索结果中不会有重复的记录如果使用union这两个结果集同样会尝试进行合并然后在输出最终结果前进行排序因此如果可以判断检索结果中不会有重复的记录时候应该用union all这样效率就会因此得到提高。 用varchar/nvarchar 代替 char/nchar 尽可能的使用 varchar/nvarchar 代替 char/nchar 因为首先变长字段存储空间小可以节省存储空间其次对于查询来说在一个相对较小的字段内搜索效率显然要高些。 不要以为 NULL 不需要空间比如char(100) 型在字段建立时空间就固定了 不管是否插入值NULL也包含在内都是占用 100个字符的空间的如果是varchar这样的变长字段 null 不占用空间。 查询select语句优化 更新Update语句优化 如果只更改1、2个字段不要Update全部字段否则频繁调用会引起明显的性能消耗同时带来大量日志 插入Insert语句优化 在新建临时表时如果一次性插入数据量很大那么可以使用 select into 代替 create table避免造成大量 log 以提高速度如果数据量不大为了缓和系统表的资源应先create table然后insert。
SQL语句的执行顺序 FROM对SQL语句执行查询时首先对关键字两边的表以笛卡尔积的形式执行连接并产生一个虚表V1。虚表就是视图数据会来自多张表的执行结果。ON对FROM连接的结果进行ON过滤,并创建虚表V2JOIN将ON过滤后的左表添加进来并创建新的虚拟表V3WHERE对虚拟表V3进行WHERE筛选创建虚拟表V4GROUP BY对V4中的记录进行分组操作创建虚拟表V5HAVING对V5进行过滤创建虚拟表V6SELECT将V6中的结果按照SELECT进行筛选创建虚拟表V7DISTINCT对V7表中的结果进行去重操作创建虚拟表V8如果使用了GROUP BY子句则无需使用DISTINCT因为分组的时候是将列中唯一的值分成一组并且每组只返回一行记录所以所有的记录都h是不同的。ORDER BY对V8表中的结果进行排序。
如何优化SQL查询
使用索引
如果查询时没有使用索引查询语句将扫描表中的所有记录。在数据量大的情况下这样查询的速度会很慢。如果使用索引进行查询查询语句可以根据索引快速定位到待查询记录从而减少查询的记录数达到提高查询速度的目的。
优化子查询
使用子查询可以进行SELECT语句的嵌套查询即一个SELECT查询的结果作为另一个SELECT语句的条件。子查询可以一次性完成很多逻辑上需要多个步骤才能完成的SQL操作。
子查询虽然可以使查询语句很灵活但执行效率不高。执行子查询时MySQL需要为内层查询语句的查询结果建立一个临时表。然后外层查询语句从临时表中查询记录。查询完毕后再撤销这些临时表。因此子查询的速度会受到一定的影响。如果查询的数据量比较大这种影响就会随之增大。
在MySQL中可以使用连接JOIN查询来替代子查询。连接查询不需要建立临时表其速度比子查询要快如果查询中使用索引性能会更好。
Mysql删除数据
Delete用来删除表的全部或者一部分数据行执行delete之后用户需要提交(commmit)或者回滚(rollback)来执行删除或者撤销删除会触发这个表上所有的delete触发器
DELETE FROM 表名 [WHERE 子句] [ORDER BY 子句] [LIMIT 子句]DELETE FROM tb_name; 删除表中的所有数据Truncate删除表中的所有数据这个操作不能回滚也不会触发这个表上的触发器TRUNCATE比delete更快占用的空间更小
TRUNCATE [TABLE] 表名Drop命令从数据库中删除表所有的数据行索引和权限也会被删除所有的DML触发器也不会被触发这个命令也不能回滚。
TRUNCATE 和 DELETE 的区别
从逻辑上说TRUNCATE 语句与 DELETE 语句作用相同但是在某些情况下两者在使用上有所区别。
DELETE 是 DML 类型的语句TRUNCATE 是 DDL 类型的语句。它们都用来清空表中的数据。DELETE 是逐行一条一条删除记录的TRUNCATE 则是直接删除原来的表再重新创建一个一模一样的新表而不是逐行删除表中的数据执行数据比 DELETE 快。因此需要删除表中全部的数据行时尽量使用 TRUNCATE 语句 可以缩短执行时间。DELETE 删除数据后配合事件回滚可以找回数据TRUNCATE 不支持事务的回滚数据删除后无法找回。DELETE 删除数据后系统不会重新设置自增字段的计数器TRUNCATE 清空表记录后系统会重新设置自增字段的计数器。DELETE 的使用范围更广因为它可以通过 WHERE 子句指定条件来删除部分数据而 TRUNCATE 不支持 WHERE 子句只能删除整体。DELETE 会返回删除数据的行数但是 TRUNCATE 只会返回 0没有任何意义。
MySQL如何优化?
针对查询我们可以通过使用索引、使用连接代替子查询的方式来提高查询速度。
针对慢查询我们可以通过分析慢查询日志来发现引起慢查询的原因从而有针对性的进行优化。
针对插入我们可以通过禁用索引、禁用检查等方式来提高插入速度在插入之后再启用索引和检查。
针对数据库结构我们可以通过将字段很多的表拆分成多张表、增加中间表、增加冗余字段等方式进行优化。
explain关注什么
重点要关注如下几列
列名备注type本次查询表联接类型从这里可以看到本次查询大概的效率。key最终选择的索引如果没有索引的话本次查询效率通常很差。key_len本次查询用于结果过滤的索引实际长度。rows预计需要扫描的记录数预计需要扫描的记录数越小越好。Extra额外附加信息主要确认是否出现 Using filesort、Using temporary 这两种情况。
另外Extra列需要注意以下的几种情况
关键字备注Using filesort将用外部排序而不是按照索引顺序排列结果数据较少时从内存排序否则需要在磁盘完成排序代价非常高需要添加合适的索引。Using temporary需要创建一个临时表来存储结果这通常发生在对没有索引的列进行GROUP BY时或者ORDER BY里的列不都在索引里需要添加合适的索引。Using index表示MySQL使用覆盖索引避免全表扫描不需要再到表中进行二次查找数据这是比较好的结果之一。注意不要和type中的index类型混淆。Using where通常是进行了全表/全索引扫描后再用WHERE子句完成结果过滤需要添加合适的索引。Impossible WHERE对Where子句判断的结果总是false而不能选择任何数据例如where 10无需过多关注。Select tables optimized away使用某些聚合函数来访问存在索引的某个字段时优化器会通过索引直接一次定位到所需要的数据行完成整个查询例如MIN()\MAX()这种也是比较好的结果之一。
表中有几千万条数据怎么办
建议按照如下顺序进行优化
优化SQL和索引增加缓存如memcached、redis读写分离可以采用主从复制也可以采用主主复制使用MySQL自带的分区表这对应用是透明的无需改代码但SQL语句是要针对分区表做优化的做垂直拆分即根据模块的耦合度将一个大的系统分为多个小的系统做水平拆分要选择一个合理的sharding key为了有好的查询效率表结构也要改动做一定的冗余应用也要改sql中尽量带sharding key将数据定位到限定的表上去查而不是扫描全部的表。
MySQL的慢查询优化有了解吗
优化MySQL的慢查询可以按照如下步骤进行
开启慢查询日志
MySQL中慢查询日志默认是关闭的可以通过配置文件my.ini或者my.cnf中的log-slow-queries选项打开也可以在MySQL服务启动的时候使用--log-slow-queries[file_name]启动慢查询日志。
启动慢查询日志时需要在my.ini或者my.cnf文件中配置long_query_time选项指定记录阈值如果某条查询语句的查询时间超过了这个值这个查询过程将被记录到慢查询日志文件中。
分析慢查询日志
直接分析mysql慢查询日志利用explain关键字可以模拟优化器执行SQL查询语句来分析sql慢查询语句。
常见慢查询优化 索引没起作用的情况 在使用LIKE关键字进行查询的查询语句中如果匹配字符串的第一个字符为“%”索引不会起作用。只有“%”不在第一个位置索引才会起作用。MySQL可以为多个字段创建索引。一个索引可以包括16个字段。对于多列索引只有查询条件中使用了这些字段中的第1个字段时索引才会被使用。查询语句的查询条件中只有OR关键字且OR前后的两个条件中的列都是索引时查询中才使用索引。否则查询将不使用索引。 优化数据库结构 对于字段比较多的表如果有些字段的使用频率很低可以将这些字段分离出来形成新表。因为当一个表的数据量很大时会由于使用频率低的字段的存在而变慢。对于需要经常联合查询的表可以建立中间表以提高查询效率。通过建立中间表把需要经常联合查询的数据插入到中间表中然后将原来的联合查询改为对中间表的查询以此来提高查询效率。 分解关联查询 很多高性能的应用都会对关联查询进行分解就是可以对每一个表进行一次单表查询然后将查询结果在应用程序中进行关联很多场景下这样会更高效。 优化LIMIT分页 当偏移量非常大的时候例如可能是limit 10000,20这样的查询这是mysql需要查询10020条然后只返回最后20条前面的10000条记录都将被舍弃这样的代价很高。优化此类查询的一个最简单的方法是尽可能的使用索引覆盖扫描而不是查询所有的列。然后根据需要做一次关联操作再返回所需的列。对于偏移量很大的时候这样做的效率会得到很大提升。
关系型和非关系型数据库的区别你了解多少
关系型数据库的优点 容易理解。因为它采用了关系模型来组织数据。可以保持数据的一致性。数据更新的开销比较小。支持复杂查询带where子句的查询 非关系型数据库的优点 不需要经过SQL层的解析读写效率高。基于键值对数据的扩展性很好。可以支持多种类型数据的存储如图片文档等等
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RQXRO6Qw-1682647034826)(D:/学习/JAVA/面经/面试题整理版本.assets/image-20220907112634255.png)]
Mysql查询语句加锁
lock in share mode 加入共享锁
for update 加入排他锁
最左匹配原则
在MySQL建立联合索引时会遵守最左前缀匹配原则即最左优先在检索数据时从联合索引的最左边开始匹配。也就是如果你的 SQL 语句中用到了联合索引中的最左边的索引那么这条 SQL 语句就可以利用这个联合索引去进行匹配
缓存与数据库的一致性
先删除redis再更新数据库先更新数据库再删除redis。两种各有各的好处。如果选择第一种那么一般就是延时双删和订阅binlog如果选择第二种实现简单但是短暂不一致。
1) 先删除缓存后更新数据库
如果有 2 个线程要并发「读写」数据可能会发生以下场景
线程 A 要更新 X 2原值 X 1线程 A 先删除缓存线程 B 读缓存发现不存在从数据库中读取到旧值X 1线程 A 将新值写入数据库X 2线程 B 将旧值写入缓存X 1
最终 X 的值在缓存中是 1旧值在数据库中是 2新值发生不一致。
可见先删除缓存后更新数据库当发生「读写」并发时还是存在数据不一致的情况。
2) 先更新数据库后删除缓存
依旧是 2 个线程并发「读写」数据
缓存中 X 不存在数据库 X 1线程 A 读取数据库得到旧值X 1线程 B 更新数据库X 2)线程 B 删除缓存线程 A 将旧值写入缓存X 1
最终 X 的值在缓存中是 1旧值在数据库中是 2新值也发生不一致。
这种情况「理论」来说是可能发生的但实际真的有可能发生吗
其实概率「很低」这是因为它必须满足 3 个条件
缓存刚好已失效读请求 写请求并发更新数据库 删除缓存的时间步骤 3-4要比读数据库 写缓存时间短步骤 2 和 5
仔细想一下条件 3 发生的概率其实是非常低的。
因为写数据库一般会先「加锁」所以写数据库通常是要比读数据库的时间更长的。
这么来看「先更新数据库 再删除缓存」的方案是可以保证数据一致性的。
所以我们应该采用这种方案来操作数据库和缓存。
好解决了并发问题我们继续来看前面遗留的第二步执行「失败」导致数据不一致的问题。
如何保证第二步成功
异步重试订阅数据库变更日志再操作缓存
具体来讲就是我们的业务应用在修改数据时「只需」修改数据库无需操作缓存。
那什么时候操作缓存呢这就和数据库的「变更日志」有关了。
拿 MySQL 举例当一条数据发生修改时MySQL 就会产生一条变更日志Binlog我们可以订阅这个日志拿到具体操作的数据然后再根据这条数据去删除对应的缓存。 订阅变更日志目前也有了比较成熟的开源中间件例如阿里的 canal使用这种方案的优点在于
无需考虑写消息队列失败情况只要写 MySQL 成功Binlog 肯定会有自动投递到下游队列canal 自动把数据库变更日志「投递」给下游的消息队列
当然与此同时我们需要投入精力去维护 canal 的高可用和稳定性。
至此我们可以得出结论想要保证数据库和缓存一致性推荐采用「先更新数据库再删除缓存」方案并配合「消息队列」或「订阅变更日志」的方式来做。
主从库延迟和延迟双删问题
到这里还有 2 个问题是我们没有重点分析过的。
第一个问题还记得前面讲到的「先删除缓存再更新数据库」方案导致不一致的场景么
这里我再把例子拿过来让你复习一下
2 个线程要并发「读写」数据可能会发生以下场景
线程 A 要更新 X 2原值 X 1线程 A 先删除缓存线程 B 读缓存发现不存在从数据库中读取到旧值X 1线程 A 将新值写入数据库X 2线程 B 将旧值写入缓存X 1
最终 X 的值在缓存中是 1旧值在数据库中是 2新值发生不一致。
第二个问题是关于「读写分离 主从复制延迟」情况下缓存和数据库一致性的问题。
在「先更新数据库再删除缓存」方案下「读写分离 主从库延迟」其实也会导致不一致
线程 A 更新主库 X 2原值 X 1线程 A 删除缓存线程 B 查询缓存没有命中查询「从库」得到旧值从库 X 1从库「同步」完成主从库 X 2线程 B 将「旧值」写入缓存X 1
最终 X 的值在缓存中是 1旧值在主从库中是 2新值也发生不一致。
看到了么这 2 个问题的核心在于缓存都被回种了「旧值」。
那怎么解决这类问题呢
最有效的办法就是把缓存删掉。
但是不能立即删而是需要「延迟删」这就是业界给出的方案缓存延迟双删策略。
按照延时双删策略这 2 个问题的解决方案是这样的
解决第一个问题在线程 A 删除缓存、更新完数据库之后先「休眠一会」再「删除」一次缓存。
解决第二个问题线程 A 可以生成一条「延时消息」写到消息队列中消费者延时「删除」缓存。
这两个方案的目的都是为了把缓存清掉这样一来下次就可以从数据库读取到最新值写入缓存。
但问题来了这个「延迟删除」缓存延迟时间到底设置要多久呢
问题1延迟时间要大于「主从复制」的延迟时间问题2延迟时间要大于线程 B 读取数据库 写入缓存的时间
但是这个时间在分布式和高并发场景下其实是很难评估的。
很多时候我们都是凭借经验大致估算这个延迟时间例如延迟 1-5s只能尽可能地降低不一致的概率。
所以你看采用这种方案也只是尽可能保证一致性而已极端情况下还是有可能发生不一致。
所以实际使用中我还是建议你采用「先更新数据库再删除缓存」的方案同时要尽可能地保证「主从复制」不要有太大延迟降低出问题的概率。
总结
关于缓存和数据库不一致的问题可以采取两种方法第一种先删除缓存在更新数据库但是在并发情况下会出现不一致的情况可以采用延迟双删的策略延时双删的基本思路如下
删除缓存更新数据库sleep N毫秒再次删除缓存。
阻塞一段时间之后再次删除缓存就可以把这个过程中缓存中不一致的数据删除掉。而具体的时间要评估你这项业务的大致时间按照这个时间来设定即可。
如果采用先更新数据库再删除缓存的话为了保证两步都成功执行需配合「消息队列」或「订阅变更日志」的方案来做本质是通过「重试」的方式保证数据一致性 订阅binlog当一条数据发生修改时MySQL 就会产生一条变更日志Binlog我们可以订阅这个日志拿到具体操作的数据然后再根据这条数据去删除对应的缓存 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qjxrcljm-1682647034827)(D:/学习/JAVA/面经/面试题整理版本.assets/image-20220903142730256.png)] 删除缓存失败的情况:增加 cache 更新重试机制常用 如果 cache 服务当前不可用导致缓存删除失败的话我们就隔一段时间进行重试重试次数可以自己定。如果多次重试还是失败的话我们可以把当前更新失败的 key存入队列中等缓存服务可用之后再将缓存中对应的 key 删除即可 在「先更新数据库再删除缓存」方案下「读写分离 主从库延迟」也会导致缓存和数据库不一致缓解此问题的方案是「延迟双删」凭借经验发送「延迟消息」到队列中延迟删除缓存同时也要控制主从库延迟尽可能降低不一致发生的概率
使用先更新数据库在删除缓存的操作cannel消息队列 流程如下图所示 1更新数据库数据 2数据库会将操作信息写入binlog日志当中 3订阅程序提取出所需要的数据以及key 4另起一段非业务代码获得该信息 5尝试删除缓存操作发现删除失败 6将这些信息发送至消息队列 7重新从消息队列中获得该数据重试操作。
Mysql的琐机制 「表锁」是粒度最大的锁表示当前的操作对整张表加锁开销小加锁快不会出现死锁但是由于粒度太大因此造成锁的冲突机率大并发性能低。Mysql中**「MyISAM储存引擎就支持表锁」MyISAM的表锁模式有两种「表共享读锁」和「表独占写锁」**。表锁被大部分的mysql引擎支持MyISAM和InnoDB都支持表级锁。 **「页锁」**的粒度是介于行锁和表锁之间的一种锁。 **「行锁」**是粒度最小的锁机制行锁的加锁开销性能大加锁慢并且会出现死锁但是行锁的锁冲突的几率低并发性能高。行锁是InnoDB默认的支持的锁机制MyISAM不支持行锁这个也是InnoDB和MyISAM的区别之一。
行锁在使用的方式上可以划分为「共享读锁S锁和排它写锁X锁」。
当一个事务对Mysql中的一条数据行加上了读锁当前事务不能修改该行数据只能执行度操作其他事务只能对该行数据加读锁不能加写锁。若是一个事务对一行数据加了写锁该事物能够对该行数据执行读和写操作其它事务不能对该行数据加任何的锁既不能读也不能写。 乐观锁和悲观锁 乐观锁乐观锁不是数据库自带的需要我们自己去实现。乐观锁是指操作数据库时(更新操作)想法很乐观认为这次的操作不会导致冲突在操作数据时并不进行任何其他的特殊处理也就是不加锁而在进行更新后再去判断是否有冲突了。通常实现是这样的在表中的数据进行操作时(更新)先给数据表加一个版本(version)字段每操作一次将那条记录的版本号加1。也就是先查询出那条记录获取出version字段,如果要对那条记录进行操作(更新),则先判断此刻version的值是否与刚刚查询出来时的version的值相等如果相等则说明这段期间没有其他程序对其进行操作则可以执行更新将version字段的值加1**如果更新时发现此刻的version值与刚刚获取出来的version的值不相等则说明这段期间已经有其他程序对其进行操作了则不进行更新操作。** 悲观锁悲观锁的实现往往依靠数据库提供的锁机制。悲观锁的实现首先实现悲观锁时我们必须先使用set autocommit0; 关闭mysql的autoCommit属性。因为我们查询出数据之后就要将该数据锁定。关闭自动提交后我们需要手动开启事务。 mysql使用的共享锁和排它锁来实现悲观锁select…lock in share mode共享锁select…for update排他锁
总结读用乐观锁写用悲观锁
数据冗余
数据冗余:在一个数据集合中重复的数据称为数据冗余
例如在设计数据库时某一字段属于一个表但它又同时出现在另一个或多个表且完全等同于它在其本来所属表的意义表示那么这个字段就是一个冗余字段。
缺点
存储空间的浪费。系统开销大,维护成本高数据的一致性难以保证当冗余字段使用的多了,数据一致性就难以保证,为了保证一致性就会产生很大的性能开销,如果某些冗余字段是采用人工维护(开发人员),数据一致性就会难以得到保障.
好处:
利用空间换时间提高查询速度可以用于数据恢复
Mysql宕机之后怎么办用哪个日志恢复具体怎么恢复的知道吗两阶段提交
使用redo log日志进行恢复 两阶段提交的第一阶段 prepare阶段写rodo-log 并将其标记为prepare状态。紧接着写binlog两阶段提交的第二阶段commit阶段写bin-log 并将其标记为commit状态。
两段提交
1、prepare阶段写redo log
2、commit阶段写binlog并且将redo log的状态改成commit状态
mysql发生崩溃恢复的过程中会根据redo log日志结合 binlog 记录来做事务回滚
1、如果redo log 和 binlog都存在逻辑上一致那么提交事务
2、如果redo log存在而binlog不存在逻辑上不一致那么回滚事务
最后大家可发现这里的两阶段提交实际是存在与redo log与binlog。所以当未开启binlog那就是提交事务直接写到redo log里面。这也就是redo log事务两阶段提交看场景区分的原因。
两段提交详细解释
在执行更新语句过程会记录redo log与binlog两块日志以基本的事务为单位**redo log在事务执行过程中可以不断写入**而binlog只有在提交事务时才写入所以redo log与binlog的写入时机不一样。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ocT4c6VB-1682647034829)(D:/学习/JAVA/面经/面试题整理版本.assets/image-20220912155444125.png)]
如果不小心删除了一个表你知道用什么恢复吗
找到bin log文件当前正在使用的binlog文件里面就有我们要恢复的数据。一般生产环境中的binlog文件都是几百M乃至上G的大小我们不能逐行去找被删除的数据在什么位置所以记住误操作的时间很重要我们可以通过mysqlbinlog命令的–start-datetime参数快速定位数据位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c9GuIdUv-1682647034829)(D:/学习/JAVA/面经/面试题整理版本.assets/image-20220820205350823.png)]
是什么是事务?事务的使用场景
事务就是一组逻辑操作要么全部成功要么全部失败
场景一如果实际的业务中需要将一条数据同时存放到两张表中 并且要求两张表中的数据同步那么此时就需要使用事务管理机制保证数据同步。如果出现错误情况比如表一插入数据成功表二插入数据失败那么就回滚终止数据持久化操作。
场景二金融行业的软件开发严格重视事务处理比如我们常见的转账操作一方的账户金额减少对应的是另一方的账户金额增加这个过程需要使用到事务机制不然转账不能成功
https://leetcode.cn/problems/bu-ke-pai-zhong-de-shun-zi-lcof/)
事务的隔离级别 脏读当前事务(A)中可以读到其他事务(B)未提交的数据脏数据这种现象是脏读。不可重复读在事务A中先后两次读取同一个数据两次读取的结果不一样这种现象称为不可重复读。脏读与不可重复读的区别在于前者读到的是其他事务未提交的数据后者读到的是其他事务已提交的数据。幻读在事务A中按照某个条件先后两次查询数据库两次查询结果的条数不同这种现象称为幻读。不可重复读与幻读的区别可以通俗的理解为前者是数据变了后者是数据的行数变了。
MVCC
MVCC全称多版本并发控制它为每个数据都根据事务维护了多个版本使得其在并发事务中解决了读写冲突同时使用快照读为mvcc提供非阻塞读功能所以他是一个用于解决读写冲突的无锁并发控制机制
1.隐藏列InnoDB每行数据中都有隐藏列隐藏列中包含了本行数据的事务id指向undo log的指针等
2.基于undo log的版本链每行数据的隐藏列中包含了指向undo log的指针而每条undo log指向了更高级的undo log从而形成了undo log的版本链
3.ReadView通过隐藏列和版本链MySQL可以将数据恢复到指针版本但是具体恢复到哪个版本需要根据ReadView确定 ReadView指事务在某个时刻给整个事务系统打快照之后在进行读写操作时会将读取到的数据中的事务id于trx_sys快照比较从而判断数据对该ReadView是否可见即对事务是否可见
ACID和锁
A原子性实现原子性的关键就是undo log日志事务在对数据库进行修改的时候会将修改的操作记录到undo log日志当中如果事务失败或者回滚的话那么读取undo log中的信息进行恢复
D持久性实现一致性的关键就是redo log当进行修改数据的时候还会redo log将记录这次修改当事务提交时会调用fsync接口对redo log进行刷盘。如果MySQL宕机重启时可以读取redo log中的数据对数据库进行恢复并且redo log都是顺序写入的
I隔离性并发情形下事务之间互不干扰
第一方面(一个事务)写操作对(另一个事务)写操作的影响锁机制保证隔离性。
隔离性是通过锁来完成的事务在修改数据之前需要先获得相应的锁。获得锁之后事务便可以修改数据。该事务操作期间这部分数据是锁定的其他事务如果需要修改数据需要等待当前事务提交或回滚后释放锁。
第二方面(一个事务)写操作对(另一个事务)读操作的影响MVCC保证隔离性。
MVCC全称Multi-Version Concurrency Control即多版本的并发控制协议。它最大的优点是读不加锁因此读写不冲突并发性能好。
原理维护了一个数据的多个版本快照读为Mysql实现MVCC提供了一个阻塞功能 隐藏列InnoDB中每行数据都有隐藏列隐藏列中包含了本行数据的事务id、指向undo log的指针等。基于undo log的版本链每行数据的隐藏列中包含了指向undo log的指针而每条undo log也会指向更早版本的undo log从而形成一条版本链。ReadView通过隐藏列和版本链MySQL可以将数据恢复到指定版本。但是具体要恢复到哪个版本则需要根据ReadView来确定。所谓ReadView是指事务记做事务A在某一时刻给整个事务系统trx_sys打快照之后再进行读操作时会将读取到的数据中的事务id与trx_sys快照比较从而判断数据对该ReadView是否可见即对事务A是否可见。 C一致性就是事务前后数据库的完整性约束不被破坏 可以说一致性是事务追求的最终目标。前面提到的原子性、持久性和隔离性都是为了保证数据库状态的一致性。此外除了数据库层面的保障一致性的实现也需要应用层面进行保障。实现一致性的措施包括 保证原子性、持久性和隔离性如果这些特性无法保证事务的一致性也无法保证。数据库本身提供保障例如不允许向整形列插入字符串值、字符串长度不能超过列的限制等。应用层面进行保障例如如果转账操作只扣除转账者的余额而没有增加接收者的余额无论数据库实现的多么完美也无法保证状态的一致。 间隙锁
间隙锁用于锁定一个范围但不包含记录本身。它的作用是为了阻止多个事务将记录插入到同一范围内而这会导致幻读问题的产生。
MVCC
MVCC多版本并发控制控制了一个数据的多个版本号实现了读写不冲突 隐藏列InnoDB中每行数据都有隐藏列隐藏列中包含了本行数据的事务id、指向undo log的指针等。基于undo log的版本链每行数据的隐藏列中包含了指向undo log的指针而每条undo log也会指向更早版本的undo log从而形成一条版本链。ReadView通过隐藏列和版本链MySQL可以将数据恢复到指定版本。但是具体要恢复到哪个版本则需要根据ReadView来确定。所谓ReadView是指事务记做事务A在某一时刻给整个事务系统trx_sys打快照之后再进行读操作时会将读取到的数据中的事务id与trx_sys快照比较从而判断数据对该ReadView是否可见即对事务A是否可见。 I一致性就是事务前后数据库的完整性约束不被破坏
MVCC➕Next-key-Lock 防止幻读
InnoDB存储引擎在 RR 级别下通过 MVCC和 Next-key Lock 来解决幻读问题
1、执行普通 select此时会以 MVCC 快照读的方式读取数据
在快照读的情况下RR 隔离级别只会在事务开启后的第一次查询生成 Read View 并使用至事务提交。所以在生成 Read View 之后其它事务所做的更新、插入记录版本对当前事务并不可见实现了可重复读和防止快照读下的 “幻读”
2、执行 select…for update/lock in share mode、insert、update、delete 等当前读
在当前读下读取的都是最新的数据如果其它事务有插入新的记录并且刚好在当前事务查询范围内就会产生幻读InnoDB 使用 Next-key Lockopen in new window 来防止这种情况。当执行当前读时会锁定读取到的记录的同时锁定它们的间隙防止其它事务在查询范围内插入数据。只要我不让你插入就不会发生幻读
什么时候会使用到间隙锁
只使用唯一索引查询并且只锁定一条记录时innoDB会使用行锁。
只使用唯一索引查询但是检索条件是范围检索或者是唯一检索然而检索结果不存在试图锁住不存在的数据时会产生 Next-Key Lock。
使用普通索引检索时不管是何种查询只要加锁都会产生间隙锁。
同时使用唯一索引和普通索引时由于数据行是优先根据普通索引排序再根据唯一索引排序所以也会产生间隙锁。
说一说你对redo log、undo log、binlog的了解
binlogBinary Log
二进制日志文件就是常说的binlog。二进制日志记录了MySQL所有修改数据库的操作然后以二进制的形式记录在日志文件中其中还包括每条语句所执行的时间和所消耗的资源以及相关的事务信息。
默认情况下二进制日志功能是开启的启动时可以重新配置--log-bin[file_name]选项修改二进制日志存放的目录和文件名称。
redo log
重做日志用来实现事务的持久性即事务ACID中的D。它由两部分组成一是内存中的重做日志缓冲redo log buffer其是易失的二是重做日志文件redo log file它是持久的。
InnoDB是事务的存储引擎它通过Force Log at Commit机制实现事务的持久性即当事务提交COMMIT时必须先将该事务的所有日志写入到重做日志文件进行持久化待事务的COMMIT操作完成才算完成。这里的日志是指重做日志在InnoDB存储引擎中由两部分组成即redo log和undo log。
redo log用来保证事务的持久性undo log用来帮助事务回滚及MVCC的功能。redo log基本上都是顺序写的在数据库运行时不需要对redo log的文件进行读取操作。而undo log是需要进行随机读写的。
undo log
重做日志记录了事务的行为可以很好地通过其对页进行“重做”操作。但是事务有时还需要进行回滚操作这时就需要undo。因此在对数据库进行修改时InnoDB存储引擎不但会产生redo还会产生一定量的undo。这样如果用户执行的事务或语句由于某种原因失败了又或者用户用一条ROLLBACK语句请求回滚就可以利用这些undo信息将数据回滚到修改之前的样子。
redo存放在重做日志文件中与redo不同undo存放在数据库内部的一个特殊段segment中这个段称为undo段undo segmentundo段位于共享表空间内。
数据库如何保证一致性
分为两个层面来说。
从数据库层面数据库通过原子性、隔离性、持久性来保证一致性。也就是说ACID四大特性之中C(一致性)是目的A(原子性)、I(隔离性)、D(持久性)是手段是为了保证一致性数据库提供的手段。数据库必须要实现AID三大特性才有可能实现一致性。例如原子性无法保证显然一致性也无法保证。从应用层面通过代码判断数据库数据是否有效然后决定回滚还是提交数据
数据库如何保证原子性
主要是利用 Innodb 的undo log。 undo log名为回滚日志是实现原子性的关键当事务回滚时能够撤销所有已经成功执行的 SQL语句他需要记录你要回滚的相应日志信息。 例如
当你delete一条数据的时候就需要记录这条数据的信息回滚的时候insert这条旧数据当你update一条数据的时候就需要记录之前的旧值回滚的时候根据旧值执行update操作当年insert一条数据的时候就需要这条记录的主键回滚的时候根据主键执行delete操作
undo log记录了这些回滚需要的信息当事务执行失败或调用了rollback导致事务需要回滚便可以利用undo log中的信息将数据回滚到修改之前的样子
数据库如何保证持久性
主要是利用Innodb的redo log。重写日志 正如之前说的MySQL是先把磁盘上的数据加载到内存中在内存中对数据进行修改再写回到磁盘上。如果此时突然宕机内存中的数据就会丢失。 怎么解决这个问题 简单啊事务提交前直接把数据写入磁盘就行啊。 这么做有什么问题
只修改一个页面里的一个字节就要将整个页面刷入磁盘太浪费资源了。毕竟一个页面16kb大小你只改其中一点点东西就要将16kb的内容刷入磁盘听着也不合理。毕竟一个事务里的SQL可能牵涉到多个数据页的修改而这些数据页可能不是相邻的也就是属于随机IO。显然操作随机IO速度会比较慢。
于是决定采用redo log解决上面的问题。当做数据修改的时候不仅在内存中操作还会在redo log中记录这次操作。当事务提交的时候会将redo log日志进行刷盘(redo log一部分在内存中一部分在磁盘上)。当数据库宕机重启的时候会将redo log中的内容恢复到数据库中再根据undo log和binlog内容决定回滚数据还是提交数据。
采用redo log的好处
其实好处就是将redo log进行刷盘比对数据页刷盘效率高具体表现如下
redo log体积小毕竟只记录了哪一页修改了啥因此体积小刷盘快。redo log是一直往末尾进行追加属于顺序IO。效率显然比随机IO来的快。
数据库的左链接和右链接
左链接:它会返回左表中的所有记录和右表中满足连接条件的记录。
右链接:它会返回右表中的所有记录和左表中满足连接条件的记录。
全外连接返回左表和右表的所有记录以及满足条件的记录
自链接:返回左右表满足条件的记录
Mysql查询语句加锁
lock in share mode 加入共享锁
for update 加入排他锁
是什么是事务?事务的使用场景
事务就是一组逻辑操作要么全部成功要么全部失败
场景一如果实际的业务中需要将一条数据同时存放到两张表中 并且要求两张表中的数据同步那么此时就需要使用事务管理机制保证数据同步。如果出现错误情况比如表一插入数据成功表二插入数据失败那么就回滚终止数据持久化操作。
场景二金融行业的软件开发严格重视事务处理比如我们常见的转账操作一方的账户金额减少对应的是另一方的账户金额增加这个过程需要使用到事务机制不然转账不能成功
https://leetcode.cn/problems/bu-ke-pai-zhong-de-shun-zi-lcof/)
Mysql常用的数据类型
数值类型 日期类型 字符串类型 MySQL 数据类型 | 菜鸟教程 (runoob.com)
大表优化
读写分离
也是目前常用的优化从库读主库写一般不要采用双主或多主引入很多复杂性尽量采用文中的其他方案来提高性能。同时目前很多拆分的解决方案同时也兼顾考虑了读写分离
缓存
垂直拆分
垂直分库是根据数据库里面的数据表的相关性进行拆分比如一个数据库里面既存在用户数据又存在订单数据那么垂直拆分可以把用户数据放到用户库、把订单数据放到订单库。垂直分表是对数据表进行垂直拆分的一种方式常见的是把一个多字段的大表按常用字段和非常用字段进行拆分每个表里面的数据记录数一般情况下是相同的只是字段不一样使用主键关联
水平拆分
水平拆分是通过某种策略将数据分片来存储分库内分表和分库两部分每片数据会分散到不同的MySQL表或库达主从复制读写分离的实现
部署多台数据库选择其中的一台作为主数据库其他的一台或者多台作为从数据库。保证主数据库和从数据库之间的数据是实时同步的这个过程也就是我们常说的主从复制。系统将写请求交给主数据库处理读请求交给从数据库处理。
主从复制的原理
MySQL binlog(binary log 即二进制日志文件) 主要记录了 MySQL 数据库中数据的所有变化(数据库执行的所有 DDL 和 DML 语句)。因此我们根据主库的 MySQL binlog 日志就能够将主库的数据同步到从库中。
主库将数据库中数据的变化写入到 binlog从库连接主库从库会创建一个 I/O 线程向主库请求更新的 binlog主库会创建一个 binlog dump 线程来发送 binlog 从库中的 I/O 线程负责接收从库的 I/O 线程将接收的 binlog 写入到 relay log 中。从库的 SQL 线程读取 relay log 同步数据本地也就是再执行一遍 SQL 。
canal 的工具canal 的原理就是模拟 MySQL 主从复制的过程解析 binlog 将数据同步到其他的数据源。
复制的工作原理并不复杂其实就是一个完全备份加上二进制日志备份的还原。不同的是这个二进制日志的还原操作基本上实时在进行中。这里特别需要注意的是复制不是完全实时地进行同步而是异步实时。这中间存在主从服务器之间的执行延时如果主服务器的压力很大则可能导致主从服务器延时较大。复制的工作原理如下图所示其中从服务器有2个线程一个是I/O线程负责读取主服务器的二进制日志并将其保存为中继日志另一个是SQL线程复制执行中继日志。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-okkvstw1-1682647034831)(D:/学习/JAVA/面经/面试题整理版本.assets/image-20220912151953665.png)]
项目中如何解决高并发
使用redis缓存缓存首页数据使用Nginx解决负载均衡的问题可以使用数据库的主从读写分离模式
到分布式的效果能够支持非常大的数据量。前面的表分区本质上也是一种特殊的库内分表
redo log日志
redo log重做日志是InnoDB存储引擎独有的它让MySQL拥有了崩溃恢复能力。
比如 MySQL 实例挂了或宕机了重启时InnoDB存储引擎会使用redo log恢复数据保证数据的持久性与完整性。 MySQL 中数据是以页为单位你查询一条记录会从硬盘把一页的数据加载出来加载出来的数据叫数据页会放入到 Buffer Pool 中。
后续的查询都是先从 Buffer Pool 中找没有命中再去硬盘加载减少硬盘 IO 开销提升性能。
更新表数据的时候也是如此发现 Buffer Pool 里存在要更新的数据就直接在 Buffer Pool 里更新。
然后会把“在某个数据页上做了什么修改”记录到重做日志缓存redo log buffer里接着刷盘到 redo log 文件里。 图片笔误提示第 4 步 “清空 redo log buffe 刷盘到 redo 日志中”这句话中的 buffe 应该是 buffer。 理想情况事务一提交就会进行刷盘操作但实际上刷盘的时机是根据策略来进行的。 小贴士每条 redo 记录由“表空间号数据页号偏移量修改数据长度具体修改的数据”组成 InnoDB 存储引擎为 redo log 的刷盘策略提供了 innodb_flush_log_at_trx_commit 参数它支持三种策略
0 设置为 0 的时候表示每次事务提交时不进行刷盘操作1 设置为 1 的时候表示每次事务提交时都将进行刷盘操作默认值2 设置为 2 的时候表示每次事务提交时都只把 redo log buffer 内容写入 page cache
innodb_flush_log_at_trx_commit 参数默认为 1 也就是说当事务提交时会调用 fsync 对 redo log 进行刷盘
另外InnoDB 存储引擎有一个后台线程每隔1 秒就会把 redo log buffer 中的内容写到文件系统缓存page cache然后调用 fsync 刷盘。
MySQL三大日志(binlog、redo log和undo log)详解 | JavaGuide
为什么要有redolog日志直接刷盘不行吗
数据页刷盘是随机写因为一个数据页对应的位置可能在硬盘文件的随机位置所以性能是很差。
如果是写 redo log一行记录可能就占几十 Byte只包含表空间号、数据页号、磁盘文件偏移量、更新值再加上是顺序写所以刷盘速度很快。
所以用 redo log 形式记录修改内容性能会远远超过刷数据页的方式这也让数据库的并发能力更强。
varchar和char的区别
1、CHAR的长度是不可变的而VARCHAR的长度是可变的也就是说定义一个CHAR[10]和VARCHAR[10],如果存进去的是‘ABCD’, 那么CHAR所占的长度依然为10除了字符‘ABCD’外后面跟六个空格而VARCHAR的长度变为4了取数据的时候CHAR类型的要用trim()去掉多余的空格而VARCHAR类型是不需要的。
2、CHAR的存取速度要比VARCHAR快得多因为其长度固定方便程序的存储与查找但是CHAR为此付出的是空间的代价因为其长度固定所以难免会有多余的空格占位符占据空间可以说是以空间换取时间效率而VARCHAR则是以空间效率为首位的。VARCHAR需要使用1或2个额外字节记录字符串的长度
3、CHAR的存储方式是一个英文字符ASCII占用1个字节一个汉字占用两个字节而VARCHAR的存储方式是一个英文字符占用2个字节一个汉字也占用2个字节。
4、两者的存储数据都是非unicode的字符数据。
varchar(255)存多少个汉字
在字符集为UTF-8的情况下 MySQL | version 4.1 VARCHAR以字节为单位存储假设全部为常用汉字则VARCHAR(255)共可存放约85个汉字。
MySQL | version 4.1 VARCHAR以字符为单位存储假设全部为常用汉字则VARCHAR(255)可以存放255个汉字。
MySQL的一级缓存和二级缓存
一级缓存缓存的是 SQL 语句二级缓存缓存的是结果对象。
一级缓存 指的是Session作用域也是在Session级别 , 在操作数据库时需要构造SQLSession的对象这个对象中可以存缓存数据而不同的SQLSession缓存数据的区域是互不影响的只能作用于在同一个Session中
一级缓存的工作原理
第一次查询在数据库查而后放在缓存里第二次查询就直接在缓存里查
如果两次查询期间有增删改的操作缓存里的数据会清空再次查询需要去数据库里查
二级缓存是mapper级别的缓存多个SqlSession去操作同一个Mapper的sql语句多个SqlSession可以共用二级缓存二级缓存是跨SqlSession的。二级缓存的作用范围更大。
二级缓存是mapper级别的缓存。使用二级缓存时多个SqlSession使用同一个Mapper的sql语句去操作数据库得到的数据会存在二级缓存区域它同样是使用HashMap进行数据存储。相比一级缓存SqlSession二级缓存的范围更大多个Sqlsession可以共用二级缓存二级缓存是跨SqlSession的。
二级缓存的作用域是mapper的同一个namespace。不同的sqlSession两次执行相同的namespace下的sql语句且向sql中传递的参数也相同即最终执行相同的sql语句则第一次执行完毕会将数据库中查询的数据写到缓存第二次查询会从缓存中获取数据不再去底层数据库查询从而提高效率。
二级缓存的工作原理 Mybatis的一级缓存和二级缓存
了解MyBatis缓存机制吗
参考答案
MyBatis的缓存分为一级缓存和二级缓存。
一级缓存
一级缓存也叫本地缓存它默认会启用并且不能关闭。一级缓存存在于SqlSession的生命周期中即它是SqlSession级别的缓存。在同一个 SqlSession 中查询时MyBatis 会把执行的方法和参数通过算法生成缓存的键值将键值和查询结果存入一个Map对象中。如果同一个SqlSession 中执行的方法和参数完全一致那么通过算法会生成相同的键值当Map 缓存对象中己经存在该键值时则会返回缓存中的对象。
二级缓存
二级缓存存在于SqlSessionFactory 的生命周期中即它是SqlSessionFactory级别的缓存。若想使用二级缓存需要在如下两处进行配置。
在MyBatis 的全局配置settings 中有一个参数cacheEnabled这个参数是二级缓存的全局开关默认值是true 初始状态为启用状态。
MyBatis 的二级缓存是和命名空间绑定的即二级缓存需要配置在Mapper.xml 映射文件中。在保证二级缓存的全局配置开启的情况下给Mapper.xml 开启二级缓存只需要在Mapper. xml 中添加如下代码
cache /二级缓存具有如下效果
映射语句文件中的所有SELECT 语句将会被缓存。映射语句文件中的所有时INSERT 、UPDATE 、DELETE 语句会刷新缓存。缓存会使用Least Rece ntly U sed ( LRU 最近最少使用的算法来收回。根据时间表如no Flush Int erv al 没有刷新间隔缓存不会以任何时间顺序来刷新。缓存会存储集合或对象无论查询方法返回什么类型的值的1024 个引用。 下的sql语句且向sql中传递的参数也相同即最终执行相同的sql语句**则第一次执行完毕会将数据库中查询的数据写到缓存第二次查询会从缓存中获取数据不再去底层数据库查询从而提高效率。
二级缓存的工作原理
[外链图片转存中…(img-zE8vYANX-1682647034832)]
Mybatis的一级缓存和二级缓存
了解MyBatis缓存机制吗
参考答案
MyBatis的缓存分为一级缓存和二级缓存。
一级缓存
一级缓存也叫本地缓存它默认会启用并且不能关闭。一级缓存存在于SqlSession的生命周期中即它是SqlSession级别的缓存。在同一个 SqlSession 中查询时MyBatis 会把执行的方法和参数通过算法生成缓存的键值将键值和查询结果存入一个Map对象中。如果同一个SqlSession 中执行的方法和参数完全一致那么通过算法会生成相同的键值当Map 缓存对象中己经存在该键值时则会返回缓存中的对象。
二级缓存
二级缓存存在于SqlSessionFactory 的生命周期中即它是SqlSessionFactory级别的缓存。若想使用二级缓存需要在如下两处进行配置。
在MyBatis 的全局配置settings 中有一个参数cacheEnabled这个参数是二级缓存的全局开关默认值是true 初始状态为启用状态。
MyBatis 的二级缓存是和命名空间绑定的即二级缓存需要配置在Mapper.xml 映射文件中。在保证二级缓存的全局配置开启的情况下给Mapper.xml 开启二级缓存只需要在Mapper. xml 中添加如下代码
cache /二级缓存具有如下效果
映射语句文件中的所有SELECT 语句将会被缓存。映射语句文件中的所有时INSERT 、UPDATE 、DELETE 语句会刷新缓存。缓存会使用Least Rece ntly U sed ( LRU 最近最少使用的算法来收回。根据时间表如no Flush Int erv al 没有刷新间隔缓存不会以任何时间顺序来刷新。缓存会存储集合或对象无论查询方法返回什么类型的值的1024 个引用。缓存会被视为read/write可读可写的意味着对象检索不是共享的而且可以安全地被调用者修改而不干扰其他调用者或线程所做的潜在修改。