学习完本文章的好处:即使只是一个开发工程师,只是 MySQL 的用户,在了解了一个个系统模块的原理后,再来使用它,感觉是完全不一样的。当在代码里写下一行数据库命令的时候,我就能想到它在数据库端将怎么执行,它的性能是怎么样的,怎样写能让我的应用程序访问数据库的性能最高。进一步,哪些数据处理让数据库系统来做性能会更好,哪些数据处理在缓存里做性能会更好,我心里也会更清楚。在建表和建索引的时候,我也会更有意识地为将来的查询优化做综合考虑,比如确定是否使用递增主键、主键的列怎样选择。学习资料:《高性能mysql》《mysql必知必回》《redis入门指南》《redis实战》《mysql实战45讲》(强烈推荐) 慕课网教学视频
mysql是最流行的关系型数据库管理系统之一,在Java企业级开发中非常常用,因为 MySQL 是开源免费的,并且方便扩展,其默认端口号是3306。
1、日进制日志log-bin //主从复制
2、错误日志log-error //默认关闭,用于记录严重的警告和错误信息,每次启动和关闭的详情
3、查询日志log //默认关闭,记录查询的sql语句,开启会降低mysql性能
4、数据文件 //路径/var/lib/mysql frm文件:存放表结构 mym文件:存放表数据 myi文件:存放表索引
5、如何配置 // /etc/my.cnf文件
理论上达到第三范式是符合要求的,但是一般在生产环境下为了数据查询方便,数据会有一定的冗余,也就是说一般达到第二范式即可。
范式的种类:
1、mysql组件
连接器: 身份认证和权限相关(登录 MySQL 的时候)。
查询缓存: 执行查询语句的时候,会先查询缓存(MySQL 8.0 版本后移除,因为这个功能不太实用)。
分析器: 没有命中缓存的话,SQL 语句就会经过分析器,分析器说白了就是要先看你的 SQL 语句要干嘛,再检查你的 SQL 语句语法是否正确。
优化器: 按照 MySQL 认为最优的方案去执行。
执行器: 执行语句,然后从存储引擎返回数据。
2、简单来说 MySQL 主要分为 Server 层和存储引擎层:
Server 层:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如 存储过程、触发器、视图,函数 等(我司一般不使用),还有一个通用的日志模块 binlog 日志模块。
存储引擎:主要负责数据的存储和读取,采用可以替换的插件式架构,支持 InnoDB、MyISAM、Memory 等多个存储引擎,其中 InnoDB 引擎有自有的日志模块 redolog 模块。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始就被当做默认存储引擎了。
3、查询语句
我们的 sql 可以分为两种,一种是查询,一种是更新(增加,更新,删除)。我们先分析下查询语句,语句如下:
select * from tb_student A where A.age='18' and A.name=' 张三 ';
结合上面的说明,我们分析下这个语句的执行流程:
先检查该语句是否有权限,如果没有权限,直接返回错误信息,如果有权限,在 MySQL8.0 版本以前,会先查询缓存,以这条 sql 语句为 key 在内存中查询是否有结果,如果有直接缓存,如果没有,执行下一步。
通过分析器进行词法分析,提取 sql 语句的关键元素,比如提取上面这个语句是查询 select,提取需要查询的表名为 parana_items,需要查询所有的列,查询条件是这个表的 id=‘1’。然后判断这个 sql 语句是否有语法错误,比如关键词是否正确等等,如果检查没问题就执行下一步。
接下来就是优化器进行确定执行方案,上面的 sql 语句,可以有两种执行方案:
a.先查询学生表中姓名为“张三”的学生,然后判断是否年龄是 18。
b.先找出学生中年龄 18 岁的学生,然后再查询姓名为“张三”的学生。
那么优化器根据自己的优化算法进行选择执行效率最好的一个方案(优化器认为,有时候不一定最好)。那么确认了执行计划后就准备开始执行了。
进行权限校验,如果没有权限就会返回错误信息,如果有权限就会调用数据库引擎接口,返回引擎的执行结果。
4、更新语句
以上就是一条查询 sql 的执行流程,那么接下来我们看看一条更新语句如何执行的呢?sql 语句如下:
update tb_student A set A.age='19' where A.name=' 张三 ';
我们来给张三修改下年龄,在实际数据库肯定不会设置年龄这个字段的,不然要被技术负责人打的。其实条语句也基本上会沿着上一个查询的流程走,只不过执行更新的时候肯定要记录日志啦,这就会引入日志模块了,MySQL 自带的日志模块式 binlog(归档日志,可以理解为git中的master分支) ,所有的存储引擎都可以使用,我们常用的 InnoDB 引擎还自带了一个日志模块 redo log(重做日志,可以理解为git中的release分支),我们就以 InnoDB 模式下来探讨这个语句的执行流程。流程如下:
5、为什么要用两个日志模块,用一个日志模块不行吗?
这是因为最开始 MySQL 并没与 InnoDB 引擎( InnoDB 引擎是其他公司以插件形式插入 MySQL 的) ,MySQL 自带的引擎是 MyISAM,但是我们知道 redo log 是 InnoDB 引擎特有的,其他存储引擎都没有,这就导致会没有 crash-safe 的能力(crash-safe 的能力即使数据库发生异常重启,之前提交的记录都不会丢失),binlog 日志只能用来归档。
并不是说只用一个日志模块不可以,只是 InnoDB 引擎就是通过 redo log 来支持事务的。那么,又会有同学问,我用两个日志模块,但是不要这么复杂行不行,为什么 redo log 要引入 prepare 预提交状态?这里我们用反证法来说明下为什么要这么做?
如果采用 redo log 两阶段提交的方式就不一样了,写完 binglog 后,然后再提交 redo log 就会防止出现上述的问题,从而保证了数据的一致性。那么问题来了,有没有一个极端的情况呢?假设 redo log 处于预提交状态,binlog 也已经写完了,这个时候发生了异常重启会怎么样呢? 这个就要依赖于 MySQL 的处理机制了,MySQL 的处理过程如下:
这样就解决了数据一致性的问题。
6、总结
参考资料:林晓斌《MySQL 实战45讲》
1、mysql视图:是一种虚拟存在的表(对视图的修改不影响基本表),通常是有一个表或者多个表的行或列的子集,具有和物理表相同的功能,可以对视图进行增,删,改,查等操作
作用:重用sql语句,简化复杂的sql操作,不必知道他的查询细节;保护数据,提高安全性
应用场景:1、多个地方用到同样的查询结果,该查询结果使用的sql语句较为复杂 如:查询邮箱中包含a字符的员工名、部门名和工种信息 /查询各部门的平均工资级别 /查询平均工资最低的部门信息
2、游标:是一个存储在MySQL服务器上的数据库查询,它不是一条 SELECT语句,而是被该语句检索出来的结果集。
在存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据。游标主要用于交互式应用,其中用户需要滚动屏幕上的数据,并对数据进行浏览或做出更改.
使用游标的原因:使用简单的 SELECT语句,例如,没有办法得到第一行、下一行或前 10行,也不存在每次一行地处理所有行的简单方法(相对于成批地处理它们)。有时,需要在检索出来的行中前进或后退一行或多行。这就是使用游标的原因
我们公司目前没有用到视图和游标。
1、什么是存储过程?存储过程是事先经过编译并存储在数据库中的一段SQL语句的集合
业务逻辑可以封装存储过程中,这样不仅容易维护,而且执行效率也高。
2、好处:减少数据在数据库和应用服务器之间的传输,提高了数据处理的效率
3、MySQL存储过程
-- 创建MySQL存储过程
drop procedure if exists pr_add; (备注:如果存在pr_add的存储过程,则先删掉
计算两个数之和(备注:实现计算两个整数之和的功能)
create procedure pr_add (a int,b int)
begin declare c int;
if a is null then set a = 0;
end if;
if b is null then set b = 0;
end if;
set c = a + b;
select c as sum; //存储过程默认自动提交
-- 调用 MySQL 存储过程
call pr_add(10, 20);
4、存储过程和存储函数的特点和区别
5、存储过程与SQL的对比?
6、你觉得存储过程和SQL语句该使用哪个?
触发器是一种与表操作有关的数据库对象,当触发器所在表上出现指定事件时,将调用该对象,即表的操作事件触发表上的触发器的执行。
CREATE TRIGGER trigger_name //触发器名称
trigger_time //触发时机
trigger_event ON tbl_name //触发事件 表名
FOR EACH ROW
trigger_stmt //触发器程序体,可以是一句SQL语句,或者用 BEGIN 和 END 包含的多条语句
可以建立6种触发器 时机,即:
BEFORE INSERT、BEFORE UPDATE、BEFORE DELETE、AFTER INSERT、AFTER UPDATE、AFTER DELETE
create trigger tri_stuInsert after insert
on student for each row
begin
declare c int;
set c = (select stuCount from class where classID = new.classID);
update class set stuCount = c + 1 where classID = new.classID;
解决乱码的核心思想是统一编码。utf8编码
SQL注入:通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句
Select * from use_info where username = “input_usr_name” and password = “” or “” = “”
举例:当执行的sql为 select * from user where username = “admin” or “a”=“a”时,sql语句恒成立,参数admin毫无意义。
防止sql注入的方式:
聚合函数是对一组值进行计算并返回单一的值的函数,它经常与select 语句中的 group by 子句一同使用。
a. avg():返回的是指定组中的平均值,空值被忽略。
b. count():返回的是指定组中的项目个数。
c. max():返回指定数据中的最大值。
d. min():返回指定数据中的最小值。
e. sum():返回指定数据的和,只能用于数字列,空值忽略。
f. group by():对数据进行分组,对执行完group by之后的组进行聚合函数的运算,计算每一组的值。
最后用having去掉不符合条件的组,having子句中的每一个元素必须出现在select列表中(只针对于mysql)
外连接:
左连接(左外连接):以左表作为基准进行查询,左表数据会全部显示出来,右表如果和左表匹配的数据则显示相应字段的数据,如果不匹配则显示为null。
右连接(右外连接):以右表作为基准进行查询,右表数据会全部显示出来,左表如果和右表匹配的数据则显示相应字段的数据,如果不匹配则显示为null。
全连接:先以左表进行左外连接,再以右表进行右外连接。
内连接:
显示表之间有连接匹配的所有行。
使用子查询,先查出所有包含XYZ的行,再使用Not Exists字段对数据进行过滤
Delete:用来删除表的全部或者一部分数据行,执行delete之后,用户需要提交(commmit)或者回滚(rollback)来执行删除或者撤销删除, delete命令会触发这个表上所有的delete触发器
Truncate:删除表中的所有数据,这个操作不能回滚,也不会触发这个表上的触发器,TRUNCATE比delete更快,占用的空间更小;
Drop:从数据库中删除表,所有的数据行,索引和权限也会被删除,所有的DML触发器也不会被触发,这个命令也不能回滚
总结:不再需要一张表的时候,用drop/想删除部分数据行时候,用delete/在保留表而删除所有数据的时候用truncate
spark相关知识点,暂时用不上
一个 SQL 执行的很慢,我们要分两种情况讨论:
1、大多数情况下很正常,偶尔很慢,则有如下原因
(1)数据库在刷新脏页,例如 redo log 写满了需要同步到磁盘。
(2)执行的时候,遇到锁,如表锁、行锁。
2、这条 SQL 语句一直执行的很慢,则有如下原因。
(1)没有用上索引:例如该字段没有索引;由于对字段进行运算、函数操作导致无法用索引。
(2)数据库选错了索引。
如何排查:
-- 判断是否真的在等待锁
show processlist
-- 查询索引的基数和实际是否符合,可以重新来统计索引的基数
analyze table t;
1、当只要一行数据时使用limit 1
查询时如果已知会得到一条数据,这种情况下加上 limit 1 会增加性能。因为 mysql 数据库引擎会在找到一条结果停止搜索,而不是继续查询下一条是否符合标准直到所有记录查询完毕。
2、选择正确的数据库引擎
Mysql中有两个引擎MyISAM和InnoDB,每个引擎有利有弊。
MyISAM :适用于一些大量查询的应用,但对于有大量写功能的应用不是很好。甚至你只需要update一个字段整个表都会被锁起来。而别的进程就算是读操作也不行要等到当前update操作完成之后才能继续进行。另外,MyISAM对于select count(* )这类操作是超级快的。
InnoDB的趋势会是一个非常复杂的存储引擎,对于一些小的应用会比MyISAM慢,但是支持“行锁”,所以在写操作比较多的时候会比较优秀。并且,它支持很多的高级应用,例如:事务管理。
3、用not exists代替not in
Not exists用到了连接能够发挥已经建立好的索引的作用,not in不能使用索引。Not in是最慢的方式要同每条记录比较,在数据量比较大的操作不建议使用这种方式。
4、对操作符的优化,尽量不采用不利于索引的操作符
如:in not in is null is not null <> 等
某个字段总要拿来搜索,为其建立索引: Mysql中可以利用alter table语句来为表中的字段添加索引,语法为:alter table表明add index(字段名);
1、怎么发现有问题的sql语句?(通过MySQL慢查询日志对有效率问题的SQL进行监控)
2、通过explain查询和分析SQL的执行计划
3、where子句中可以对字段进行 null 值判断吗?
最好不要给数据库留null,含有空值的列很难进行查询优化(你应该用0、一个特殊的值或者一个空串代替空值)
4、select * from admin left join log on admin.admin_id = log.admin_id where log.admin_id>10 如何优化?(政采云)
可以把join优化为子查询:select * from (select * from admin where admin_id>10) T1 left join log on T1.admin_id = log.admin_id。
使用JOIN 时候,应该用小的结果驱动大的结果(left join 左边表结果尽量小,如果有条件应该放到左边先处理,right join 同理反向)同时尽量把牵涉到多表联合的查询拆分多个query
5、limit的基数比较大时使用 between 政采云考到了哈哈,幸好我早有准备
例如:select * from admin order by admin_id limit 100000,10 用了id主键做索引更快
- 使用order by + between and优化为:select * from admin where admin_id between 100000 and 100010 order by admin_id。(不要使用*,想查询什么数据就全部查出来)
- 也可使用子查询优化为:select id,title from collect where id >= (select id from collect order by id limit 90000,1) limit 10; 解释原因:limit 10000,10的意思是扫描满足条件的10010行,扔掉前面的10000行,返回最后20行
优化方案:
1、子查询优化法
先找出第一条数据,然后大于等于这条数据的id就是要获取的数据
缺点:数据必须是连续的,可以说不能有where条件,where条件会筛选数据,导致数据失去连续性
2、尽量使用复合索引
对于有where条件,又想走索引用limit的,必须设计一个索引,将where放在第一位,limit用到的主键放在第2位,而且只能select主键。
3、使用id限定优化
这种方式假设数据表的id是连续递增的,则我们根据查询的页数和查询的记录数可以算出查询的id的范围,可以使用id between and来查询
select * from orders_history where type = 2 and id between 1000000 and 1000100 limit 100;
还有另外一种写法:
select * from orders_history where id >= 1000001 limit 100;
6、尽量避免在列上做运算,这样导致索引失效
-- 例如:
select * from admin where year(admin_time) > 2014;
-- 优化为:
select * from admin where admin_time > '2014-01-01′
总结:
SELECT id,title,content FROM items WHERE id IN (SELECT id FROM items ORDER BY id limit 900000,10)
-- 如果limit语句的offset较大,你可以通过传递pk键值来减少offset=0,这个主键最好是int型并且auto_increment
SELECT * FROM users WHERE uid > 456891 ORDER BY uid LIMIT 0, 10;
7、order by关键字优化 尽量使用index,否则会使用filesort,效率低
8、group by关键字优化 先排序后进行分组 按照索引建的最佳左前缀
9、开启慢查询日志 日志分析工具mysql dump slow
10、show profile 是mysql提供的分析当前会话中语句执行的资源消耗情况,可以用于sql调优的测量
1)数据库设计方面
1、对查询进行优化,应尽量避免全表扫描,首先应考虑在where及order by涉及的列上建立索引
2、应尽量避免在where子句中对字段进行null值判断,否则将导致引擎放弃使用索引而进行全表扫描
3、并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用
4、索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了insert及update的效率
5、应尽可能的避免更新索引数据列,因为索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源
*不再需要的数据行,可以设置他的status为false
6、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了
7、尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为变长字段存储空间小
8、不再索引列上做任何操作(计算、函数、类型装换),会导致全表扫表
2) SQL语句方面
1、应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描;
2、应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描;
3、in 和 not in 也要慎用,否则会导致全表扫描;
4、%abc 模糊查询中,%放在前面也会导致全表扫描;
如何解决?覆盖索引(是一种特殊的多列索引,当多列索引指向一个查询语句中所有的字段时,该多列索引就被称为覆盖索引,覆盖索引可以避免回表,减少树的搜索次数)
例如(身份证号,姓名)作为联合索引实现覆盖索引
在建立联合索引的时候,如何安排索引内的字段顺序?
第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的;
第二个原则:考虑的就是空间;
5、如果在 where 子句中使用参数,也会导致全表扫描;
6、应尽量避免在where子句中对字段进行函数操作;
7、不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引;
8、任何地方都不要使用 select * from t ,用具体的字段列表代替"* ";
9、尽量避免向客户端返回大数据量,可以使用数据库的物理分页 可以使用mybatis的分页插件pagehelper,对SQL数据进行物理分页;
10、在查找数据时,如果是确认满足要求的数据只有一条,可以使用limit 1关键字,这样可以在查找到该数据后,不进行下一步的查找,节省效率;
案例分析:
例子1:select * from T where k between 3 and 5,需要执行几次树的搜索操作,会扫描多少行?(k为普通索引,id为主键索引)
1. 在k索引树上找到k=3的记录,取得 ID = 300;
2. 再到ID索引树查到ID=300对应的R3;
3. 在k索引树取下一个值k=5,取得ID=500;
4. 再回到ID索引树查到ID=500对应的R4;
5. 在k索引树取下一个值k=6,不满足条件,循环结束 在这个过程中,回到主键索引树搜索的过程,我们称为回表。
可以看到,这个查询过程读了k索引树的3条记录(步骤1、3和5),回表了两次(步骤2和4)。
如果执行的语句是select ID from T where k between 3 and 5,这时只需要查ID的值,而ID的值已经在k索引树上了,因此可以直接提供查询结果,不需要回表。
例子2:(最左前缀法则)如果多个字段设为一个索引a,b,c,查询的时候只使用a,b来查询,这样可以吗?***
对于复合索引:MySQL从左到右使用索引中的字段,一个查询可以只使用索引中的一部分,但只能是最左侧部分。例如索引是key index(a,b,c) 可以支持a|a,b|a,b,c,3种组合进行查找,但是不支持b,c进行查找。
例子3:(索引下推)MySQL 5.6 引入的索引下推优化(index condition pushdown),可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
select * from tuser where name like ‘张%’ and age=10 and ismale=1;
并行复制功能也是5.6才引入
3) java方面:重点内容
1、尽可能的少造对象;
2、合理摆正系统设计的位置。大量数据操作,和少量数据操作一定是分开的 orm搞不定大量数据操作;
3、控制好内存,让数据流起来,而不是全部读到内存再处理,而是边读取边处理; (分页操作时要考虑)
4、合理利用缓存(mybatis的二级缓存)
(1)选择合适数据类型
使用较小的数据类型解决问题;
使用简单的数据类型(mysql处理int要比varchar容易);
尽可能的使用not null 定义字段;
尽量避免使用text类型,非用不可时最好考虑分表;
(2)表的范式的优化
一般情况下,表的设计应该遵循三大范式
(3)表的垂直拆分(类目属性和协作属性可以这样拆分)
把含有多个列的表拆分成多个表,解决表宽度问题,具体包括以下几种拆分手段:
把不常用的字段单独放在同一个表中/把大字段独立放入一个表中/把经常使用的字段放在一起;
这样做的好处是非常明显的,具体包括:拆分后业务清晰,拆分规则明确、系统之间整合或扩展容易、数据维护简单。
(4)表的水平拆分(mycat中讲得更清楚)
表的水平拆分用于解决数据表中数据过大的问题,水平拆分每一个表的结构都是完全一致的。一般地,将数据平分到N张表中的常用方法包括以下两种:
5) 系统配置的优化
操作系统配置的优化:增加TCP支持的队列数
mysql配置文件优化:Innodb缓存池设置(innodb_buffer_pool_size,推荐总内存的75%)和缓存池的个数(innodb_buffer_pool_instances)
6) 硬件的优化
CPU:核心数多并且主频高的
内存:增大内存
磁盘配置和选择:磁盘性能
现象 | 排查方向 | 故障降低措施 |
---|---|---|
1 pinpoint 打点存在大批量300ms以上 | 排查方向:检查db是否存在慢sql/定位调用链中具体接口/pinpoint inspector查看是否存在fullGC/应用dubbo线程池是否耗尽) 打点存在大批量红点(排查方向:确认红点出现源头,判断自己调用其他的业务服务是否存在超时) | 重启应用 |
2 应用 cpu突然升高 | 排查方向:应用是否存在fullGC/检查应用流量是否突增/是否存在不合理调用(可参考监控大盘) 1、阈值标准:pinpoint最近5min出现大批量超过5s以上的打点/应用cpu 90%以上持续3min/应用内存90%以上持续3min(瞬间暴增除外)/收到上游最多两个以上业务方反馈 | 2、解决措施:打印线程栈:jstack l <路径文件名.bin>/dump jvm堆 jmap dump:format =路径文件名.bin/重启应用:运维执行命令/jenkins下游业务服务方响应超时 sentinel consumer限流/如果存在恶意攻击是,联系安全部门拦截/如果单业务流量大,针对url或dubbo进行限流/升级应用机器配置(一般是加机器) |
3 内存升的很快 | 排查方向:1、排查是否存在新版本发布的影响;2、查询下调用频率较高的接口是否存在内存缓存的使用;3、查询下日志确定应用是否存在大批量的异常抛出 | 2、解决措施:打印线程栈:jstack l <路径文件名.bin>/dump jvm堆 jmap dump:format =路径文件名.bin/重启应用:运维执行命令/jenkins下游业务服务方响应超时 sentinel consumer限流/如果存在恶意攻击是,联系安全部门拦截/如果单业务流量大,针对url或dubbo进行限流/升级应用机器配置(一般是加机器) |
4 数据库 cpu持续90%以上10分钟 | ops是否明显增高(是否爬虫等异常流量)/是否存在大批量数据查询(索引/动态sql) | 阈值标准:数据库cpu100%持续2min且没有下降趋势/链接数一直增加没有下降的趋势,iops打满且没有下降的趋势/内存使用率80%以上持续5min,解决措施:定向业务限流/运维kill慢sql/运维kill锁进程/重启应用/升级配置(但是需要考虑升级时间的影响问题) |
5 连接数线性增高且长时间不下 | 查看是否存在慢sql/查看应用的数据库连接池配置/查看数据库中是否存在死锁 show engine innodb status; | 解决措施:定向业务限流/运维kill慢sql/运维kill锁进程/重启应用/升级配置(但是需要考虑升级时间的影响问题) |
6 iops飙高 | ddl操作 是否针对大表新增索引/是否存在变更大表字段,业务上是否存在大批量操作(写入或读取) | 解决措施:定向业务限流/运维kill慢sql/运维kill锁进程/重启应用/升级配置(但是需要考虑升级时间的影响问题) |
7 内存升高 | 确认下数据库连接数是否异常(连接也占用内存) | 解决措施:定向业务限流/运维kill慢sql/运维kill锁进程/重启应用/升级配置(但是需要考虑升级时间的影响问题) |
8 中间件 MQ 消息堆积 | 基本不需要处理 | |
10 canal delay延时高 | 排查方向:大批量数据操作,是否存在和其他业务共用canal,受其他业务影响 | 阈值标准:garafa canal delay 延迟超过用户不可接受的范围 |
包括:
最上层的服务不是mysql独有的,大多数基于网络的客户端/服务器的工具或服务都有类似的架构。比如连接处理,授权认证,安全等。
第二层架构:server层(连接器、查询缓存、分析器、优化器、执行器等)
mysql的核心服务功能,包括查询解析、分析、优化、缓存以及所有的内置函数(日期,时间,数学和加密函数)
所有跨存储引擎的功能都在这一层实现:存储过程,触发器,视图等
1、mysql会解析查询、并创建内部数据结构(解析树),然后对其进行各种优化,包括重写查询、决定表的读取顺序,以及选择合适的索引。
2、对于select语句,在解析查询之前,服务器会先检查查询缓存,如果能找到对应的查询,服务器就不必在执行查询解析、优化和执行的整个过程,而是直接返回查询缓存中的结果集。
第三层:存储引擎层(InnoDB、MyISAM、Memory等多个存储引擎)
存储引擎负责mysql中数据的存储和提取。服务器通过api和存储引擎进行通信。接口屏蔽了不同存储引擎间的差异。
存储引擎api包含几十个底层函数,用于执行诸如“开始一个事务”或“根据主键提取一行记录”等操作。但存储引擎不会去解析sql,
不同存储引擎之间也不会相互通信,而只是简单地响应上层服务器的请求。
1、连接器 Connectors
客户端和连接服务,主要完成一些类似于连接处理、授权认证及相关的安全方案。与其他编程语言中的SQL语句进行交互
细节:连接到数据库上后,首先就是连接器,用于跟客户端建立连接、获取权限、维持和管理连接 mysql -h i p − P ip -P ip−Pport -u$user -p
客户端如果太长时间没动静,连接器就会自动将它断开,这个时间是由参数wait_timeout控制的,默认是8小时
Q:长连接后,你可能会发现,有些时候MySQL占用内存涨得特别快,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是MySQL异常重启
A:两种方案
1、定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。
2、如果你用的是MySQL 5.7或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。
2. 查询缓存 Cache&Buffers
MySQL拿到一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果可能会以key-value对的形式,被直接缓存在内存中。key是查询的语句,value是查询的结果
如果查询缓存有命中的查询结果key,查询语句就可以直接去查询缓存中取数据value。这个缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,key 缓存,权限缓存等
Q:但是大多数情况下我会建议你不要使用查询缓存,为什么呢?因为查询缓存往往弊大于利
A:缓存失效太频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。除非业务中有静态表,很长时间才更新
mysql> select SQL_CACHE * from T where ID=10;显示指定查询缓存 MYSQL8.0直接将查询缓存功能删除了
3. 分析器 Parser
作用:SQL命令传递到解析器的时候会被分析器验证和解析。 缓存失效~
主要功能:
1)词法分析 将SQL语句分解成数据结构,并将这个结构传递到后续步骤,后面SQL语句的传递和处理就是基于这个结构的;
2)语法分析 如果在分解构成中遇到错误,那么就说明这个sql语句是不合理的,语句将不会继续执行下去 You have an error in your SQL syntax
4. 优化器 Optimizer
作用:SQL语句在查询之前会使用查询优化器对查询进行优化(索引、关联查询顺序)
5、执行器
开始执行语句,判断有没有执行查询的权限;如果有权限,就打开表继续执行,执行器就会根据表的引擎定义,去使用这个引擎提供的接口,最后将执行结果集返回给客户端。
SQL接口 SQL Interface
作用:接受用户的 SQL 命令,并且返回用户需要查询的结果
Management Services & Utilities
系统管理和控制工具
Connection Pool
连接池。作用:管理缓冲用户连接,线程处理等需要缓存的需求。
6、存储引擎 Engine
存储引擎是 MySQL 中具体的与文件打交道的子系统。也是 MySQL 最具有特色的一个地方。
MySQL 的存储引擎是插件式的。它根据 MySQL AB公司提供的文件访问层的一个抽象接口来定制一种
文件访问机制(这种访问机制就叫存储引擎)
重要的日志模块:redo log (innodb独有 大小固定)
关键点就是先写日志,再写磁盘
当有一条记录需要更新的时候,InnoDB引擎就会先把记录写到redo log(粉板)里面,并更新内存,这个时候更新就算完成了。
同时,InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做.
功能:InnoDB可以用redoLog保证即使数据库发生异常重启,之前提交的记录都不会丢失,称为crash-safe
重要的日志模块:binlog(server层的日志 归档日志)
binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1”
Binlog有两种模式,statement 格式的话是记sql语句,row格式会记录行的内容,记两条,更新前和更新后都有。
更新时:引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处于prepare状态。然后告知执行器执行完成了,随时可以提交事务
执行器生成这个操作的binlog,并把binlog写入磁盘;
执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更新完成
两阶段提交(这事务状态保持逻辑上的一致)
将redo log的写入拆成了两个步骤:prepare和commit,这就是"两阶段提交"
反证:如果不用两阶段提交,会发生什么?
1、先写redolog后写binlog
2、先写binlog后写redo log 如果不使用“两阶段提交”,那么数据库的状态就有可能和用它的日志恢复出来的库的状态不一致
需要搭建一些备库来增加系统的读能力的时候,现在常见的做法也是用全量备份加上应用binlog来实现的,这个“不一致”就会导致你的线上出现主从数据库不一致的情况
1、from子句 组装来自不同数据源的数据;
2、where子句 基于指定的条件对记录行进行筛选;
3、group by子句将数据划分为多个分组;
4、使用聚集函数进行计算;
5、使用having子句筛选分组;
6、计算所有的表达式;
7、select 的字段; (查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序 id同:由上至下 id不同:子查询id越大优先级越高)
8、使用order by对结果集进行排序
以上每个步骤都会产生一个虚拟表,该虚拟表被用作下一个步骤的输入。这些虚拟表对调用者(客户端应用程序或者外部查询)不可用。只有最后一步生成的表才会会给调用者。如果没有在查询中指定某一个子句,
将跳过相应的步骤
逻辑查询处理阶段简介:
1、 FROM: 对FROM子句中的前两个表执行笛卡尔积(交叉联接),生成虚拟表VT1。
2、 ON: 对VT1应用ON筛选器,只有那些使为真才被插入到TV2。
3、 OUTER (JOIN):如果指定了OUTER JOIN(相对于CROSS JOIN或INNER JOIN),保留表中未找到
匹配的行将作为外部行添加到VT2,生成TV3。如果FROM子句包含两个以上的表,则对上一个联接生成的
结果表和下一个表重复执行步骤1到步骤3,直到处理完所有的表位置。
4、 WHERE: 对TV3应用WHERE筛选器,只有使为true的行才插入TV4。
5、 GROUP BY: 按GROUP BY子句中的列列表对TV4中的行进行分组,生成TV5。
6、 CUTE|ROLLUP:把超组插入VT5,生成VT6。
7、 HAVING: 对VT6应用HAVING筛选器,只有使为true的组插入到VT7。
8、 SELECT: 处理SELECT列表,产生VT8。
9、 DISTINCT: 将重复的行从VT8中删除,产品VT9。
10、 ORDER BY: 将VT9中的行按ORDER BY子句中的列列表顺序,生成一个游标(VC10)。
11、 TOP: 从VC10的开始处选择指定数量或比例的行,生成表TV11,并返回给调用者。 where子句中的条件书写顺序
Q:select * from table t where size > 10 group by size order by size的sql语句执行顺序?
A:执行顺序将是 from table t > where size>10 > group by size > select >order by size
1、什么是索引?
索引是对数据库表中一个或多个列的值进行排序的数据结构,以协助快速查询、更新数据库表中数据。往往以索引文件的形式存储在磁盘上(索引加速了数据访问,因为存储引擎不会再去扫描整张表得到需要的数据)
通常的索引指的是B数结构组织的索引(多路搜索树)包括聚集索引,次要索引,复合索引,前缀索引,唯一索引默认使用B+数索引
2、索引的优点
优点:查找排序快、主键索引保证唯一(O(logn) 的时间复杂度)
缺点:存储空间、维护时间
使用场景:查询多、更新少、空值少的数据;
1、索引种类:
普通索引和唯一性索引:索引列的值的唯一性
唯一索引:可以保证行数据的唯一性,不允许其中任何两行具有相同索引值的索引(允许空)
create unique index indexName ON mytable(cliumnname(length));
drop index indexname ON mytable;
单个索引和复合索引:索引列所包含的列数
单值索引:即一个索引只包含单个列,一个表可以有多个单列索引
复合索引:即一个索引包含多个列
聚簇索引与非聚簇索引:聚簇索引按照数据的物理存储进行划分的
聚集索引(主键索引):提供更快的数据访问速度(堆划分),表中行的物理顺序与键值索引顺序相同。一个表只能包含一个聚集索引
非聚集索引(非主键索引):是把一个很大的范围,转换成一个小的地图,然后你需要在这个小地图中找你要寻找的信息的位置,最后通过这个位置,再去找你所需要的记录
覆盖索引定义:如果查询条件使用的是普通索引(或是联合索引的最左原则字段),查询结果是联合索引的字段或是主键,不用回表操作,直接返回结果,减少IO磁盘读写读取正行数据
最左前缀:联合索引的最左 N 个字段,也可以是字符串索引的最左 M 个字符
联合索引:根据创建联合索引的顺序,以最左原则进行where检索,比如(age,name)以age=1或age=1 and name=‘张三’可以使用索引,单以name=‘张三’ 不会使用索引,考虑到存储空间的问题,还请根据业务需求,将查找频繁的数据进行靠左创建索引。
索引下推:like 'hello%’and age >10 检索,MySQL5.6版本之前,会对匹配的数据进行回表查询。5.6版本后,会先过滤掉age<10的数据,再进行回表查询,减少回表率,提升检索速度
2、主键、自增主键、主键索引与唯一索引概念区别(按数据的逻辑进行划分)
主键:指字段唯一、不为空值的列(主键是一种特殊的唯一性索引,其可以是聚集索引,也可以是非聚集索引) 对于没有主键的表,innodb会默认创建一个Rowid做主键
主键索引:要求主键值唯一,创建主键的时候,数据库默认会为主键创建一个唯一索引(叶子节点存的是整行数据)
非主键索引:叶子节点内容是主键的值(二级索引)
自增主键:字段类型为数字、自增、并且是主键;
唯一索引:索引列的值必须唯一,但允许有空值。主键是唯一索引,这样说没错;但反过来说,唯一索引也是主键就错误了,因为唯一索引允许空值,主键不允许有空值,所以不能说唯一索引也是主键
Q:基于主键索引和普通索引的查询有什么区别?
A:如果语句是select * from T where ID=500,即主键查询方式,则只需要搜索ID这棵B+树;如果语句是select * from T where k=5,即普通索引查询方式,则需要先搜索k索引树,得到ID的值为500,再到ID索引树搜索一次。这个过程称为回表。也就是说,基于非主键索引的查询需要多扫描一棵索引树。
Q: 重建普通索引k和重建主键索引id,对于两个重建索引的做法,说出你的理解?
A: 如果删除,新建主键索引,会同时去修改普通索引对应的主键索引,性能消耗比较大。删除重建普通索引貌似影响不大,不过要注意在业务低谷期操作,避免影响业务。可以使用sql语句alter table T engine = InnoDB替代重建过程。
Q: “N叉树”的N值在MySQL中是可以被人工调整的么?
A: 可以按照调整key的大小的思路来说:如果你能指出来5.6以后可以通过page大小来间接控制应该能加分吧,面试回答不能太精减计算方法、前缀索引什么的一起上。
1、主键自动建立唯一索引
2、频繁作为查询条件的字段(经常出现在order by, group by, distinct后面的字段)
3、查询中与其它表关联的字段,外键关系建立索引
4、单键/组合索引选择? 高并发下选择组合索引
5、查询中排序的字段,统计,分组的字段
不适合:
1、频繁增删改的字段不适合
2、where条件里用不到的不创建
3、表记录太少时
4、字段重复数据太多时,如性别表
1、hash索引:给表的某一列计算hash值,排序在hash数组上,hash索引可以一次定位,效率高(等值比较,适用于redis/memcached)
缺点:因为递增主键的存储位置不是连续的,所以哈希索引做区间查询的速度很慢。
2、有序数组索引:往中间插入一个记录就必须得挪动后面所有的记录,成本太高,只适用于静态存储引擎(通常不怎么变更的数据)
3、btree索引:是以B+树为存储结构实现的,B+树是一个平衡的多叉树,从根节点到每个叶子结点的高度差值不超过1,而且同层级的节点间有指针相互连接。基于索引的顺序扫描时,可以利用双向指针快速左右移动,效率非常高,用于数据库,文件系统中。
3、hash和btree区别:
1、hash索引比较的是经过hash计算的值,所以只能进行等值比较,不能用于范围查询;***
2、hash值映射的真正数据在hash表中就不一定按照顺序排序,所以无法利用hash索引来加速任何排序操作。以及like “xx%”这样的部分模糊查询。
3、hash索引也不支持多列联合索引
无论是二叉搜索树还是AVL树,当数据量比较大时,都会由于树的深度过大而造成I/O读写过于频繁,进而导致查询效率低下,因此对于索引而言,多叉树结构成为不二选择。随着数据库技术的发展,跳表、LSM树等数据结构也被用于引擎设计中(数据库底层存储的核心就是基于这些数据模型)
聚簇索引,在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个聚集索引, 即如果存在聚集索引,就不能再指定CLUSTERED 关键字
非簇索引,在非聚集索引中,数据库表中记录的物理顺序与索引顺序可以不相同。一个表中只能有一个聚集索引,但表中的每一列都可以有自己的非聚集索引
B树:多路平衡查找树(结点的最大值m称为B_TREE的阶,简称为m叉树)
特点:平衡(叶子节点位于同一层上,到达任意叶子节点的搜索代价相同)、层少、升序
树中每个结点最多有m个孩子结点/若根结点不是叶子节点,则根结点至少有2个孩子结点/除根结点外,其它结点至少有(m/2的上界)个孩子结点
结构:父节点、子节点指针数组、key信息数组(查找值或折半)
n c0 d1 c1 d2 c2 … dn cn 有序//n为结点中关键字个数/di(1<=i<=n)为该结点的n个关键字值的第i个/ci(0<=i<=n)为该结点孩子结点的指针
所有的叶结点都在同一层上,并且不带信息(说明节点不存在)
优点:由于B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近(文件系统和数据库)
B+树(innoDB存储引擎使用的索引结构)
介绍:在InnoDB中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。InnoDB使用了B+树索引模型,所以数据都是存储在B+树中的
结构区别:
非叶节点仅含有其子树根结点中最大(或最小)关键码、叶子节点保存key数组、有序链表(依关键码的大小自小而大的顺序链接)
所有数据地址必须要到叶子节点才能获取到,所以每次数据查询的次数都一样(除了非终端结点上的关键码等于给定值这种情况)
通常在B+树上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点。
因此可以对B+树进行两种查找运算:一种是从最小关键字起顺序查找,另一种是从根节点开始,进行随机查找
性能区别:IO少、平衡、范围查找,每一个关键字的查询效率相当
B+与普通二叉树比较 IO少:
层数少、层间局部性原理、顺序读写
缺点:(索引维护)
会产生大量的随机IO,随着新数据的插入,按照B+Tree算法,新增加一个数据页,叶子节点会慢慢分裂,逻辑上连续的叶子节点在物理上往往不连续.
解决方案:
1、LSM树 HBase使用LSM树(Log-Structured Merge Tree),同样支持增删读改、顺序扫描操作,而且通过批量存储技术规避磁盘随机写入的问题,LSM树牺牲了部分读性能,用来大幅提高写性能。
LSM设计思想:将对数据的修改增量保持在内存中,达到指定大小限制后将这些修改操作批量写入磁盘。把一棵大树拆分成N棵小树,首先写入内存中,随着小树越来越大,内存中的小树会flush到磁盘中,磁盘中的树定期可以做merge操作,合并成一棵大树,以优化读性能。
2、使用自增主键(从性能和存储空间方面考量)
每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂;有业务逻辑的字段做主键,则往往不容易保证有序插入,这样写数据成本相对较高
1、局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。程序运行期间所需要的数据通常比较集中
2、B树的节点大小设置为一页(4k),减少层间索引次数,效率较高;
一次检索最多需要h-1次I/O,渐进复杂度为O(h)=O(logdN)
实际应用中,出度d是非常大的数字,通常超过100,因此h非常小
3、红黑树等高度较高,索引次数较多,即磁盘IO次数较多,性能较慢
4、B+树所有的关键字都出现在叶子节点,内存中可以存入更多的关键字,减少磁盘I/O次数
5、B+树叶子节点通过链表相连,链表中的关键字是有序的,适合范围查找(在数据库中基于范围的查询是非常频繁的,而B树只能中序遍历所有节点,效率太低)
对高度为h的m阶B树,新结点一般是插在第h层
以“%(表示任意0个或多个字符)”开头的LIKE语句,模糊匹配;
OR语句前后没有同时使用索引
数据类型出现隐式转化(如varchar不加单引号的话可能会自动转换为int型)
对于多列索引,必须满足最左匹配原则(eg:多列索引col1、col2和col3,则索引生效的情形包括 col1或col1,col2或col1,col2,col3)
经常作查询选择的字段
经常作表连接的字段
经常出现在order by, group by, distinct后面的字段
非空字段:应该指定列为NOT NULL,含有空值的列很难进行查询优化(你应该用0、一个特殊的值或者一个空串代替空值)
取值离散大的字段:(变量各个取值之间的差异程度)的列放到联合索引的前面
索引字段越小越好:数据库的数据存储以页为单位一页存储的数据越多一次IO操作获取的数据越大效率越高
1、Mysql存储引擎有哪些?(show engines来查看mysql支持的存储引擎)
2、InnoDB、Mysaim的特点和区别?
3、MyISAM与InnoDB区别?
举例如下:转账操作。假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。
1、MySQL事务特性(数据库事务transanction正确执行的四个基本要素)ACID,原子性(Atomicity)、一致性(Correspondence)、隔离性(Isolation)、持久性(Durability)
不可重复度和幻读区别:
不可重复读的重点是修改,幻读的重点在于新增或者删除。
例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。
例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读。
什么是隔离级别?数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题
作用:如果没有采取必要的隔离级别,会导致各种并发问题(脏读:读了没有被提交的数据,不可重复读:读取的数据某些字段被更新了,幻读:读取数据的表被增加/减少了数据)
隔离级别的种类(以Innodb为例:基于MVCC和锁)
读未提交(READ UNCOMMITTED):就是事务可以读取其它事务未提交的数据。 //脏读,不可重复读,幻读都会出现
读已提交(READ COMMITTED):允许事务读取已经被其他事务提交的变更数据 //sqlserver oracle默认 我司使用该级别 可避免脏读,但不可重复读,幻读都会出现
可重复读(REPEATABLE READ):(mysql默认)保证同一个事务中的多次相同的查询的结果是一致的,在事务期间,禁止其他事务对这个字段进行更新 //可避免脏读和不可重复读,但幻读仍然存在 (mysql的间隙锁已解决了幻读的问题)
解决方法:读取数据加行锁
使用场景:银行账单的数据校对
串行化(SERIALIZABLE):在事务期间,禁止其他事务对这个字段进行插、删、改操作 //所有并发问题都可以解决
方法:事务串行化顺序执行,读用共享读锁,写用排他写锁,读锁和写锁互斥,若果使用where语句,还会获取间隙锁
隔离级别 | 脏读 | 不可重复读 | 幻影读 |
---|---|---|---|
READ-UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。我们可以通过SELECT @@tx_isolation;命令来查看,MySQL 8.0 该命令改为SELECT @@transaction_isolation;
与 SQL 标准不同的地方在于InnoDB 存储引擎在 REPEATABLE-READ(可重读) 事务隔离级别下,允许应用使用 Next-Key Lock 锁算法来避免幻读的产生。这与其他数据库系统(如 SQL Server)是不同的。所以说虽然 InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读),但是可以通过应用加锁读(例如 select * from table for update 语句)来保证不会产生幻读,而这个加锁度使用到的机制就是 Next-Key Lock 锁算法。从而达到了 SQL 标准的 SERIALIZABLE(可串行化) 隔离级别。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容)*,但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)并不会有任何性能损失。
InnoDB 存储引擎在 分布式事务 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。
使用 2 个命令行mysql ,模拟多线程(多事务)对同一份数据的脏读问题。
MySQL 命令行的默认配置中事务都是自动提交的,即执行SQL语句后就会马上执行 COMMIT 操作。如果要显式地开启一个事务需要使用命令:START TARNSACTION。
我们可以通过下面的命令来设置隔离级别。
SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
我们再来看一下我们在下面实际操作中使用到的一些并发控制语句:
不可重复读
还是刚才上面的读已提交的图,虽然避免了读未提交,但是却出现了,一个事务还没有结束,就发生了 不可重复读问题
防止幻读(可重复读)
一个事务对数据库进行操作,这种操作的范围是数据库的全部行,然后第二个事务也在对这个数据库操作,这种操作可以是插入一行记录或删除一行记录,那么第一个是事务就会觉得自己出现了幻觉,怎么还有没有处理的记录呢? 或者 怎么多处理了一行记录呢?
幻读和不可重复读有些相似之处 ,但是不可重复读的重点是修改,幻读的重点在于新增或者删除
参考资料:《MySQL技术内幕:InnoDB存储引擎》
事务的ACID是通过InnoDB日志和锁来保证。
事务的隔离性是通过数据库锁的机制实现的,持久性通过redo log(重做日志)来实现,原子性和一致性通过Undo log来实现。UndoLog的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为UndoLog)。然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态。
和Undo Log相反,RedoLog记录的是新数据的备份。在事务提交前,只要将RedoLog持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是RedoLog已经持久化。系统可以根据RedoLog的内容,将所有数据恢复到最新的状态
什么是分布式事务?
分布式事务产生的原因?
从上面本地事务来看,我们可以看为两块,一个是service产生多个节点,另一个是resource产生多个节点。
分布式事务解决方案
太多团队一个人维护几个微服务,太多团队过度设计,搞得所有人疲劳不堪,而微服务过多就会引出分布式事务,这个时候我不会建议你去采用下面任何一种方案,而是请把需要事务的微服务聚合成一个单机服务,使用数据库的本地事务。因为不论任何一种方案都会增加你系统的复杂度,这样的成本实在是太高了,千万不要因为追求某些设计,而引入不必要的成本和复杂度。
结论:能不用分布式事务就不用,如果非得使用的话,结合自己的业务分析,看看自己的业务比较适合哪一种,是在乎强一致,还是最终一致即可。上面对解决方案只是一些简单介绍,如果真正的想要落地,其实每种方案需要思考的地方都非常多,复杂度都比较大,所以最后再次提醒一定要判断好是否使用分布式事务。
1、CAP定理,又被叫作布鲁尔定理。对于设计分布式系统来说(不仅仅是分布式事务)的架构师来说,CAP就是你的入门理论.
CAP理论中C是consistency强一致性,对某个指定的客户端来说,读操作能返回最新的写操作。对于数据分布在不同节点上的数据上来说,如果在某个节点更新了数据,那么在其他节点如果都能读取到这个最新的数据,那么就称为强一致,如果有某个节点没有读取到,那就是分布式不一致。
A是availablity可用性,非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。可用性的两个关键一个是合理的时间,一个是合理的响应。合理的时间指的是请求不能无限被阻塞,应该在合理的时间给出返回。合理的响应指的是系统应该明确返回结果并且结果是正确的,这里的正确指的是比如应该返回50,而不是返回40。
P是partition tolerance分区容错性 当出现网络分区后,系统能够继续工作。打个比方,这里个集群有多台机器,有台机器网络出现了问题,但是这个集群仍然可以正常工作。
2、数据库的写实时性和读实时性需求(数据库事务一致性需求)
3、对复杂的SQL查询,特别是多表关联查询的需求?
4、BASE是为了解决关系数据库强一致性引起的问题而引起的可用性降低而提出的解决方案
指标:基本可用(Basically Available)软状态(Soft state)最终一致(Eventually consistent)
它的思想是通过让系统放松对某一时刻数据一致性的要求来换取系统整体伸缩性和性能上改观。为什么这么说呢,缘由就在于大型系统往往由于地域分布和极高性能的要求,不可能采用分布式事务来完成这些指标。
5、分布式和集群
方法:将启动参数tx-isolation的值设置成READ-COMMITTED。你可以用show variables来查看当前的值
mysql5.7引入了transaction_isolation用来替换tx_isolation(我司使用该语法),到8.0.3就去除掉后者了。show variables like ‘transaction_isolation’;
1、在MySQL中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值
回滚日志 最新值
readviewA readviewB readviewC
将2改为1<-- 将3改为2 <--将4改为3<--当前值4 (同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC))
对于read-viewA,要得到1,就必须将当前值依次执行图中所有的回滚操作得到
2、什么时候删除?
在不需要的时候才删除,就是当系统里没有比这个回滚日志更早的read-view的时候
注意:
-1、尽量不要使用长事务,长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。
注意:可以在information_schema库的innodb_trx这个表中查询长事务,比如下面这个语句,用于查找持续时间超过60s的事务
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60;
在开发过程中,尽可能的减小事务范围,少用长事务,如果无法避免,保证逻辑日志空间足够用,并且支持动态日志空间增长。监控Innodb_trx表,发现长事务报警。
事务的四个特点:ACID里面, 原子性和持久性做不到,隔离性只能实现基本用不上的串行化(在异常崩溃时也无法保证) 这样的事务不要也罢
我们学习了数据库的事务及其事务的隔离级别,但是数据库是怎样隔离事务的呢?这时候就牵连到了数据库锁。当插入数据时,就锁定表,这叫做”锁表”;当更新数据时,就锁定行,这叫做”锁行”。当多个用户对数据库进行操作时,会带来数据不一致的情况,所以,锁主要是在多用户情况下保证数据库数据完整性和一致性。
1、锁的分类
锁模式分类 | 乐观锁、悲观锁 |
---|---|
范围锁 | 行锁、表锁 |
算法锁 | 临间锁、间隙锁、记录锁 |
属性锁 | 共享锁(S)、排他锁(X) |
状态锁 | 意向共享锁、意向排他锁 |
1、从对数据库事务操作的粒度来划分;
锁的粒度主要有以下几种类型–》 都是悲观锁
2、从对数据操作的类型(读/写)来划分;
总结:Mysql中的锁机制基本上都是采用的悲观锁来实现的。
3、行锁实现
行锁就是一锁锁一行或者多行记录,mysql的行锁是基于索引加载的,所以行锁是要加在索引响应的行上,即命中索引
如上图所示,数据库表中有一个主键索引和一个普通索引,Sql语句基于索引查询,命中两条记录。此时行锁一锁就锁定两条记录,当其他事务访问数据库同一张表时,被锁定的记录不能被访问,其他的记录都可以访问到。
行锁的特征:锁冲突概率低,并发性高,但是会有死锁的情况出现。
窗口A先修改了id为3的用户信息后,还没有提交事务,此时窗口B再更新同一条记录,然后就提示Lock wait timeout exceeded; try restarting transaction ,由于窗口A迟迟没有提交事务,导致锁一直没有释放,就出现了锁冲突,而窗口B一直在等待锁,所以出现了超过锁定超时的警告了。
2、表锁实现
表锁就是一锁锁一整张表,在表被锁定期间,其他事务不能对该表进行操作,必须等当前表的锁被释放后才能进行操作。表锁响应的是非索引字段,即全表扫描,全表扫描时锁定整张表,sql语句可以通过执行计划看出扫描了多少条记录。
3、在mysql中,行锁又衍生了其他几种算法锁,分别是 记录锁、间隙锁、临键锁;我们依次来看看这三种锁,什么是记录锁呢
3.1 记录锁
上面我们找到行锁是命中索引,一锁锁的是一张表的一条记录或者是多条记录,记录锁是在行锁上衍生的锁,记录锁的特征:
记录锁:记录锁锁的是表中的某一条记录,记录锁的出现条件必须是精准命中索引并且索引是唯一索引,如主键id。
3.2 间隙锁
间隙锁又称之为区间锁,每次锁定都是锁定一个区间,隶属行锁。既然间隙锁隶属行锁,那么,间隙锁的触发条件必然是命中索引的,当我们查询数据用范围查询而不是相等条件查询时,查询条件命中索引,并且没有查询到符合条件的记录,此时就会将查询条件中的范围数据进行锁定(即使是范围库中不存在的数据也会被锁定),我们通过代码演示一下:
首先,我们打开两个窗口,在窗口A中我们根据id做一个范围更改操作,不提交事务,然后在范围B中插入一条记录,该记录的id值位于窗口A中的条件范围内,我们看看运行效果:
程序报错:Lock wait timeout exceeded; try restarting transaction 。这就是间隙锁的作用。间隙锁只会出现在可重复读的事务隔离级别中,mysql5.7默认就是可重复读。间隙锁锁的是一个区间范围,查询命中索引但是没有匹配到相关记录时,锁定的是查询的这个区间范围,上述代码中,所锁定的区间就是 (1,3]这个区间,不包含1,但是包含3,并且不包含4,也就是说这里是一个左开右闭的区间。
如果将隔离级别改为 读已提交,测试间隙锁,会发现间隙锁没有生效。
设置事务隔离级别为不可重复读
set session transaction isolation level read committed;
查看当前事务级别
SELECT @@tx_isolation
3.3 临键锁
mysql的行锁默认就是使用的临键锁,临键锁是由记录锁和间隙锁共同实现的,上面我们学习间隙锁时,间隙锁的触发条件是命中索引,范围查询没有匹配到相关记录。而临键锁恰好相反,临键锁的触发条件也是查询条件命中索引,不过,临键锁有匹配到数据库记录;
间隙锁所锁定的区间是一个左开右闭的集合,而临键锁锁定是当前记录的区间和下一个记录的区间。
从上图我们可以看到,数据库中只有三条数据1、5、7,当修改范围为1~8时,则锁定的区间为(1,+∞),锁定额不单是查询范围,并且还锁定了当前范围的下一个范围区间,此时,查询的区间8,在数据库中是一个不存在的记录值,并且,如果此时的查询条件是小于或等于8,也是一样的锁定8到后面的区间。
如果查询的结尾是一个存在的值,此时又会怎样呢?现在数据库有三条数据id分别是1、5、7,我们查询条件改为大于1小于7再看看。
我们可以看到,由于7在数据库中是已知的记录,所以此时的锁定后,只锁定了(1,7],7之后的数据都没有被锁定。我们还是可以正常插入id为8的数据及其后面的数据。所以,临键锁锁定区间和查询范围后匹配值很重要,如果后匹配值存在,则只锁定查询区间,否则锁定查询区间和后匹配值与它的下一个值的区间
3.4 为什么会出现这种情况呢?为什么临键锁后匹配会这样呢?
在这里,我们不妨看看mysql的索引是怎么实现的,前面文章中有提到树结构,mysql的索引是基于B+树实现的,每个树节点上都有多个元素,即关键字数,当我们的索引树上只有1、5、7时,我们查询1~8,这个时候由于树节点关键字中并没有8,所以就把8到正无穷的区间范围都给锁定了。
那么,如果我们数据库中id有1、5、7、10,此时我们再模糊匹配id为1~8的时候,由于关键字中并没有8,所以找比8大的,也就找到了10,根据左开右闭原则,此时10也是被锁定的,但是id为11的记录还是可以正常进行插入的。
2、怎么判断是行锁还是表锁? 20181016 有赞
3、乐观锁,悲观锁
乐观锁和悲观锁的区别?不是mysql或数据库中独有的概念,而是并发编程的基本概念
悲观锁(synchronized):每次拿数据的时候都觉得数据会被人更改,所以拿数据的时候就把这条记录锁掉,这样别人就没法改这条数据了,一直到你的锁释放
关系型数据库,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁,在数据库上的悲观锁需要数据库本身提供支持,即通过常用的select … for update操作来实现悲观锁。当数据库执行select…for update时会获取被select中的数据行的行锁,因此其他并发执行的select…for update如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果
问题是: select… for update语句执行中所有扫描过的行都会被锁上,这一点很容易造成问题
乐观锁:MVCC
即很乐观,查询数据的时候总觉得不会有人更改数据,等到更新的时候再判断这个数据有没有被人更改,有人更改了则本次更新失败
SELECT data AS old_data, version AS old_version FROM …;
//根据获取的数据进行业务操作,得到new_data和new_version
UPDATE SET data = new_data, version = new_version WHERE version = old_version
if (updated row > 0) {
// 乐观锁获取成功,操作完成
} else {
// 乐观锁获取失败,回滚并重试}
悲观锁与乐观锁的应用场景
数据库衍生的职位:
数据库应用工程师,很多业务开发者就是这种定位,综合利用数据库和其他编程语言等技能,开发业务应用 java相关
数据库工程师,更加侧重于开发数据库、数据库中间件等基础软件 java相关
数据库管理员(DBA),这是一个单独的专业领域
java与数据库交互的技术:JDBC JPA/Hibernate MyBatis、SpringJDBC Template
一般解决办法就是使用锁和事务的联合机制
1、尽可能让所有数据检索都通过索引来完成,避免无缩影行锁升级为表锁
2、合理设计索引,尽量缩小锁的范围
3、尽可能减小检索条件,避免间隙锁
4、尽量控制事务大小,减少锁定资源量和时间长度
1、什么是分布式事务?
一次大的操作由不同的小操作组成,这些小操作分布在不同的服务器上,属于不同的应用。
分布式事务是为了保障不同数据库的数据一致性。
2、分布式事务产生的原因?
3、MySQL事务特性
事务:事务就是一组原子性的SQL查询,或者说一个独立的工作单元。
数据库事务transanction正确执行的四个基本要素。
ACID,原子性(Atomicity)、一致性(Correspondence)、隔离性(Isolation)、持久性(Durability)
4、分布式事务在电商系统中的应用场景
5、分布式事务解决方案
3、TCC编程模式(两阶段提交的变形)
将业务逻辑分为三块:try预留业务资源(类似DML锁定资源),confirm确认执行业务操作(类似commit),cancel取消执行业务操作(类似rollback)
以下单为例:try阶段会扣除库存,confirm阶段更新订单状态,如果更新订单失败,会进入cancel阶段,恢复库存
TCC开源框架:tcc-transaction (补偿性分布式事务框架)
1、mysql集群本质上是一个存储方案
其具有不共享(不共享的对等节点可以令集群中任意一台服务器上的更新操作,立即在其他服务器上可见)、
分布式结点架构的特点,可以提供较好的容错性,并提高性能。
在数据更新的过程中,
使用 读已提交隔离级别 保证所有结点的数据一致性,
使用 两阶段提交机制 保证所有结点都有相同的数据(如果任何一个写操作事变,则更新失败)
2、数据存储 (分布式事务)
Mysql Cluster采用同步复制实现数据节点组内的主从同步,以此来保证组内节点数据的一致性。
同步过程主要通过 两阶段提交机制 实现:
第一阶段:准备提交
1)执行提交语句时,事务将会被Master(主)发送给所有的Slave(从),每个Slave开始准备提交事务。
2)Slave准备事务完毕后向Master发送OK或ABORT消息,告知Master事务的准备情况。
第二阶段:通过收到的消息判断提交或中止
3) Master 接收所有 Slave 发送的 OK 或 ABORT 消息
Master 如果收到所有 Slave 的 OK 消息,就会向所有 Slave 发送提交消息,执行提交事务
Master 如果收到来自任何一个Slave的ABORT消息,就会向所有 Slave 发送 ABORT消息,取消提交事务。
4) Slave接收Master发送的提交或取消请求
Slave 收到提交请求,执行提交事务,并向Master发送事务已提交的确认;
Slave 收到取消请求,则会撤销所有改变并释放所占用的资源,中止事务,并向Master发送事务已中止的确认
5) Master 收到所有 Slave 的确认后,则将报告该事务被提交(或中止),然后继续处理下一个事务
总结:同步复制共需 4 次消息传递,所以 Mysql Cluster 在数据更新的速度上比单机Mysql 要慢
1、海量数据的存储问题
传统的关系性数据库已经无法满足快速查询与插入数据的需求。这个时候NoSQL的出现暂时解决了这一危机。它通过降低数据的安全性,减少对事务的支持,减少对复杂查询的支持,来获取性能上的提升。有些场合NoSQL是无法满足使用场景的,比如有事务与安全指标的。从单机mysql->memcached+mysql+垂直拆分->mysql主从复制->分库分表+水平拆分+mysql集群
2、如果使用关系型数据库解决海量存储的问题呢?
此时就需要做数据库集群,为了提高查询性能将一个数据库的数据分散到不同的数据库中存储。 使用mycat来管理数据库集群。
3、mysql性能瓶颈分析
数据库连接数,默认是100个连接数
单表数据量大,阿里规定单表数据量500万条 数据量大,IO操作就多
硬件资源(QPS/TPS每秒查询书,每秒事务量)
4、大数据量数据库性能的解决方案?
分库分表 索引优化(数据量1个亿且查询不太复杂时,没什么问题) 读写分离
1、数据库分片
2、读写分离(主从模式):
3、总结:
3、什么是mycat?
面向企业应用开发的“大数据集群”,支持事务、ACID、一个数据库中间件产品(作为后面mysql集群的proxy使用,默认端口:8066)
作用:提高可用数据分片集群,支持读写分离,支持mysql双主多从,一主(写数据库)多从(读数据库)
当添加一条记录时要向哪个数据库中插入呢?这些问题处理起来都是非常的麻烦。这种情况下可以使用一个数据库中间件mycat来解决相关的问题
用法:现在把数据库中所有的item一千五百万本(图书表)分片存储到三个数据节点中。可以是三台mysql数据库。
节点一:db1存储500万条数据 节点二:db2 500万;节点三:db3 500万;
如何分配:1、根据主键id按段切分 2、根据id取模运算
配置schema。xml文件,管理mycat的逻辑库,表,分片规则,数据节点,数据源 rule= auto-sharding-long mycat会根据此规则自动分片
读写分离:一个写节点master后面跟着多个读节点。读节点的数量取决于系统的压力。写数据库和读数据库建立主从复制,使用mysql厂商自带的binlog,然后建立mycat和mysql的心跳检查。
使用:mysql主数据库修改my.conf文件,musqld下面添加数据库及IP的配置信息 状态:File/position/binlog_Do_DB/binlog_ignore-DB。 从数据库修改my.conf文件 change master to master_host=’’,master_post=’’,master_user=’’,master_password=’‘master_log_file=’’,master_log_pos=’’
当slave_io_Running:YES且slave_SQL_Running:YES,说明配置成功
mycat配置 支持mysql主从复制状态绑定的读写分离机制 balance writeType switchType
4、中间件操作数据库的步骤
1、概念:逻辑库(使用mycat来管理)db_user,db_store 逻辑表:分片表user,全局表(数据字典 冗余),ER表(user地址 只能存放在单一的数据库中,跟着主表一起走),非分片表(门店表,店员表)
应用程序分为db_user(用户表、数据字典,用户地址表)和db_store(订单表,店员表)
经常使用的数据不变性,在后台的每个mysql中都保存一份(如数据字典最好冗余查询**)
用户表进行模2运算,store表进行主从复制
2、conf目录下,有三个关键的配置文件
scheme.xml管理mycqt实例中的逻辑库,表,分片规则,DataNode,DataSource
server.xml
rule.xml 定义了我们队表进行拆分所设计到的规则定义
有连续分片和离散分片两种
连续分片扩容无需迁移数据,范围查询资源消耗小 但是存在热点数据问题
离散分片:分片均匀,并发强 缺点:移植性差
3、现有系统如何使用mycat?
当当、美团正在做这方面的工作
mysql Dump命令生成backup.sql文件, 控制台:mysql -f backup.sql或Source backup.sql
解决方案:
1、用好ER表,子表跟随父表
2、善用全局表 冗余数据
3、注解方式
如何保证强一致性
两阶段提交 弱XA的两阶段提交
TCC补偿机制
后续补充
后续补充
后续补充
使用双机热备进行跨地区容灾。双机热备,简单来讲,就是令两个数据库保持状态自动同步。对于处于双机热备当中的数据库,对其中任何一个进行操作都可以自动同步到另外一个,这样,就保持两个数据库当中的数据始终一致。
好处:
备份工作原理:
两个原始数据相同的数据库 A 和 B,A 数据库中执行过的 sql 语句在B数据库里也同步执行一遍,通过这样,A、B数据库就可以一直保持同步
类似于读写分离的技术,使用了binlog加mycat来管理。
对于一个 Mysql 服务器,一般有两个线程负责复制和被复制,当开启复制之后:
1、什么是连接池(Connection pooling)?
2、连接池有哪些:
class B implements A{
private A a;
public B(A a){
this.a = a;
}
/* 以下是增强的方法 */
public void close() throws SQLException {
//将调用当前close的链接Connection对象添加到连接池中
this.pool.add(this);
}
/* 以下是不需要增强的方法 */
public void commit() throws SQLException {
this.conn.commit();
}
将连接添加到连接池中,添加的连接被装饰者类包装过的
```java
MyConnection myConn = new MyConnection(conn,pool);
pool.add(myconn);
//接口的实现类
Connection conn = JdbcUtils.getConnction();
2、DBCP(tomcat)是apache上的一个java连接池项目,也是tomcat使用的连接池组件,需要两个包:commons-dbcp,commons-pool,应用程序需要建立数据库连接时直接到连接池中申请一个就行,用完后放回去
核心API:basicDataSource BasicDataSourceFactory
参数:驱动,url,用户名密码,初始化连接数,最大连接数,最小空闲连接,超时等待时间
注意:现在tomcat使用的连接池是 tomcat jdbc connection pool
3、C3P0连接池的使用(hibernate/spring)
核心API:comboPooledDataSource
4、dbutils DBUtils是java编程中的数据库操作实用工具,DBUtils封装了对JDBC的操作,简化了JDBC操作,可以少写代码
需要导入的包:commons-dbutils-1.6
dbutils三大核心功能
5、Druid
1、druid简介
阿里出品,淘宝和支付宝专用数据库连接池,结合了c3p0和DBCP的优点,同时加入了日志监控,可以很好地监控连接池连接和SQL执行情况
2、druid组成
包括三个部分 1、基于filter-chain模式的插件体系;2、druidDataSource 高效可管理的数据库连接池;3、SQL parser
3、druid功能简介
4、最佳配置
<! -- 配置初始化大小、最小、最大 -->//通常来说,只需要修改initialSize、minIdle、maxActive
<property name="initialSize" value="1" />
<property name="minIdle" value="1" />
<property name="maxActive" value="20" />
poolPreparedStatements参数 //是否缓存preparedStatement,性能会有大概20%的提升 对于分库分表较多的数据库,此参数建议配置为false
5、特有功能说明
1、ExceptionSorter(无需额外配置)
当网络断开或者数据库服务器Crash时,连接池里面会存在“不可用连接”,连接池需要一种机制剔除这些“不可用连接”。
在Druid和JBoss连接池中,剔除“不可用连接”的机制称为ExceptionSorter
原理:
根据异常类型/Code/Reason/Message来识别“不可用连接”
3、连接池的机制是:
1、程序初始化时创建连接池
2、使用时向连接池申请可用连接
3、使用完毕,将连接返还给连接池
4、程序退出时,断开所有连接,并释放资源
优点:连接池还减少了用户必须等待建立与数据库的连接的时间
4、数据库连接池的使用
1、导包 c3p0 mchange-commons-java
2、初始化代码放在ConnectionManager构造函数中
2、在使用连接池时,只在第一次初始化时,比较耗时,完成初始化之后,使用连接池进行数据库操作明显比不使用连接池花费的时间少
5、连接池的性能对比
功能 | DBCP | Druid | c3p0 | tomcat-jdbc | HikariCP |
---|---|---|---|---|---|
1、是否支持PSCache | 是 | 是 | 是 | 否 | 否 |
2、监控 | jmx | jmx/log/http | jmx/log | jmx | jmx |
3、扩展性 | 弱 | 好 | 弱 | 弱 | 弱 |
4、sql拦截/解析 | 无 | 支持 | 无 | 无 | 无 |
5、代码 | 简单 | 中等 | 复杂 | 简单 | 简单 |
6、特点 | 依赖于common-pool | 阿里开源 代码逻辑复杂 | 优化力度大,功能简单 | 功能全面 且不易维护 | 起源于boneCP |
7、连接池管理 | LinkedBlockingDeque | 数组 | FairBlockingQueue | threadlocal+CopyOnWriteArrayList |
总结:
性能方面
HikariCP>Druid>tomcat-jdbc>dbcp>c3p0。HikariCP的高性能得益于最大限度的避免锁竞争,Druid功能最为全面,sql拦截等功能,统计数据较为全面,具有良好的扩展性。综合性能,扩展性等方面,可考虑使用druid或者hikariCP连接池
6、连接池中的连接是长连接还是短连接?
长连接适合于要进行大量数据传输的情况:如数据库,redis,mencache,使用长连接而长时间没有对数据库进行操作,在timeout后,mysql server就会关闭此链接,但是在高并发的情况下,使用短连接更合适
7、连接池中的连接时基于什么协议的连接?为什么?
Http连接池:
对于JDBC而言,每条单独的语句都是一个事务,即每个语句后都隐含一个commit(Connection 提供了一个auto-commit的属性来指定事务何时结束)
try{
conn.setAutoCommit(false); //将自动提交设置为false
ps.executeUpdate("修改SQL"); //执行修改操作
ps.executeQuery("查询SQL"); //执行查询操作
conn.commit(); //当两个操作成功后手动提交
}catch(Exception e) {
conn.rollback(); //一旦其中一个操作出错都将回滚,使两个操作都不成功
e.printStackTrace();
} //为了能够将多条SQL当成一个事务执行,必须首先通过Connection关闭auto-commit模式,然后通过Connection的setTransactionIsolation()方法设置事务的隔离级别,
//最后分别通过Connection的commit()方法和rollback()方法来提交事务和回滚事务
定位:基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了mysql。
原理:canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议,mysql master收到dump请求,开始推送binary log给slave(也就是canal),canal解析binary log对象(原始为byte流)
数据过滤:支持通配符的过滤模式,表名,字段内容等
数据路由/分发:解决1:n (1个parser对应多个store的模式)
数据归并:解决n:1 (多个parser对应1个store)
数据加工:在进入store之前进行额外的处理,比如join
canal 由Java开发,分为服务端和客户端,拥有众多的衍生应用,性能稳定,功能强大;canal 需要自己编写客户端来消费canal解析到的数据。
maxwell相对于canal的优势是使用简单,它直接将数据变更输出为json字符串,不需要再编写客户端。
Databus是一种低延迟变化捕获系统,已成为LinkedIn数据处理管道不可或缺的一部分。Databus解决了可靠捕获,流动和处理主要数据更改的基本要求。Databus提供以下功能:
数据传输服务(Data Transmission Service,简称DTS)是阿里云提供的一种支持 RDS(关系型数据库)、NoSQL、OLAP 等多种数据源之间数据交互的数据流服务。DTS提供了数据迁移、实时数据订阅及数据实时同步等多种数据传输能力,可实现不停服数据迁移、数据异地灾备、异地多活(单元化)、跨境数据同步、实时数据仓库、查询报表分流、缓存更新、异步消息通知等多种业务应用场景,助您构建高安全、可扩展、高可用的数据架构。
优势:数据传输(Data Transmission)服务 DTS 支持 RDBMS、NoSQL、OLAP 等多种数据源间的数据传输。它提供了数据迁移、实时数据订阅及数据实时同步等多种数据传输方式。相对于第三方数据流工具,数据传输服务 DTS 提供更丰富多样、高性能、高安全可靠的传输链路,同时它提供了诸多便利功能,极大得方便了传输链路的创建及管理。
个人理解:就是一个消息队列,会给你推送它包装过的sql对象,可以自己做个服务去解析这些sql对象。免去部署维护的昂贵使用成本。DTS针对阿里云RDS(在线关系型数据库)、DRDS等产品进行了适配,解决了Binlog日志回收,主备切换、VPC网络切换等场景下的订阅高可用问题。同时,针对RDS进行了针对性的性能优化。出于稳定性、性能及成本的考虑,推荐使用。
第一点:性能优化专题
每天早晨我都会对着镜子问自己,“如果今天是我生命中的最后一天,我还会做我今天要做的这些事吗?”如果连续很多天的答案都是“不”,我知道需要做出改变了,记住自己终将会死去,是我最重要的自我检验,并帮助我做出了许多人生中的重要决定,因为几乎任何事,所有的期望,所有的荣耀,对失败的恐惧,一切都会在死亡面前淡去,只留下真正重要的东西,时刻提醒自己将会死去,便可以帮助你避开担心失去身外之物的恐惧,你本就一无所有,没有理由不去追随你的内心,求知若饥,虚心若愚。 --史蒂夫·乔布斯