针对数据库方面:
1.基础
2.索引
3.sql语句编写
4.优化
5.可扩展以及高可用(涉及一些分库分表以及动态缩扩容的相关解决方案)
6.安全性
针对第2点:
所有的索引优化我们可以从索引本身的数据结构来进行分析(B+树,顺序排列)
关于索引:
为什么使用B-Tree(B+Tree)
上文说过,红黑树(深度比较大,也即高度太高,导致io次数很多)等数据结构也可以用来实现索引,但是文件系统及数据库系统普遍采用B-/+Tree作为索引结构,这一节将结合计算机组成原理相关知识讨论B-/+Tree作为索引的理论基础。
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,所以评价一个数据结构作为索引的优劣最重要的指标就是在查找过程中磁盘I/O操作次数的渐进复杂度。换句话说,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数。
索引的数据结构(整个mysql的innodb索引的底层数据结构实现最值得看的一篇文章)
(参考:张洋:MySQL索引背后的数据结构及算法原理)
B+树:
1. 内节点不存储data,只存储key;叶子节点不存储指针。
2.带有顺序访问指针的B+Tree
Mysql索引的实现:
不同存储引擎所使用的索引不一致的问题
MyISAM与innoDB
MyISAM:叶节点的data域存放的是数据记录的地址
InnoDB:
1.第一个重大区别是InnoDB的数据文件本身就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。
2.第二个与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域
另外,MyISAM与innoDB的其他几个区别(也得注意一下)
1.InnoDB不支持FULLTEXT类型的索引。
2.InnoDB中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*)语句包含where条件时,两种表的操作是一样的。
3.对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。
4.DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。
5.LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用。
(关于两者的一个总结:
基本上我们可以考虑使用InnoDB来替代我们的MyISAM引擎了,因为InnoDB自身很多良好的特点,比如事务支持、存储过程、视图、行级锁定等等,在并发很多的情况下,相信InnoDB的表现肯定要比MyISAM强很多。如果不是很复杂的Web应用,非关键应用,还是可以继续考虑MyISAM的)
索引选择性与前缀索引
我们常提到的一些索引优化属于结构优化
InnoDB的主键选择与插入优化:
当有一个自增的主键时:
这样就会形成一个紧凑的索引结构,近似顺序填满。由于每次插入时也不需要移动已有数据,因此效率很高,也不会增加很多开销在维护索引上。
但是当非顺序时,近似于随机,那么mysql可能需要移动索引的位置等增加很多额外的开销
针对第4点:
其中有个小问题就是limit慢的问题:
limit10000,20的意思扫描满足条件的10020行,扔掉前面的10000行,返回最后的20行,问题就在这里。
LIMIT 451350 , 30 扫描了45万多行,怪不得慢的数据库都堵死了。
但是
limit 30 这样的语句仅仅扫描30行。
那么如果我们之前记录了最大ID,就可以在这里做文章
一般是 加上where index_id>xxx order by id limit x,y 索引(index_id, id)
针对第5点:
高可用方面涉及到的分表操作
1.首先要保证针对数据库的操作都要收拢到一起
2.分表所采用的方案
数据库层面 利用分区或者merge存储引擎【需要时myISAM,但是这个不支持事务,而且要改表的 存储类型会有很多风险】
应用层面 对表进行水平或者垂直分
水平分表的话需要考虑的就是何种方式,有什么优点以及缺点(不易扩展,同时要做好主键可能相同的出现在不同表中的情况,要做提前去重操作),同时对跨表的查询应该如何考虑
(跨表的话目前的思路主要就是产生一个中间层,而不是直接去底层查
1.采用中间缓存层 (redis记录中间信息)
2.采用搜索系统如es等记录中间结果,而无需走数据库
3.采用冗余表、汇总表,通过这些表来拿到基本信息
4.【终极解决方案】既然分表就要分的彻底,那么就应该杜绝这种跨表操作,所以可以由应用层修改业务逻辑,自己去缓存中间的结果进行分步查询
)
垂直拆分的话需要将主键放在不同的分表中以方便后面的联合查询
3.分表时底层的parseSQL的编写(参数kztheme的写法)
面对对象的思想:
1.检查字段
2.或者表名(利用show tables like ‘%table_name%’来查看表是否存在)
3.组装字段(BuildQuery,分为buildField,buildWhere)
buildField考虑别名问题
buildWhere考虑各种条件:
[{a: 1}, {b: 2}] => `a` = 1 AND `b` = 2
* ['a = 1', 'b > 2'] => a = 1 AND b > 2
* {a: 1} => `a` = 1
另外一个问题就是,分表后的动态缩扩容的相关解决方案,比如分了1024各表,如果想再分表应该怎么处理?
解决方案1:如何设计可以动态扩容缩容的分库分表方案?解决方案2:一种可以避免数据迁移的分库分表scale-out扩容方式(这个就是分范围,将原来的1024个表还是以原来的hash函数来映射,然后大于这个范围的以另外的一种函数来映射)
网上针对sql优化、问题排查的总结:
作者:待风宜月
链接:https://www.zhihu.com/question/50171821/answer/119698996
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
影响Mysql 查询类SQL(注意不是count(1) count(*) count(字段)这种SQL中的语法)执行性能的,我理解大致有以下几个影响因子(无并发更新):
1.Mysql版本
2.所用引擎
3.表结构(尤其是索引的构建情况)
4.数据规模
5.数据在不同索引下的数据分布情况(也就是索引是否合理)
6.是否force index
7.blabla..(暂时想不到了 - -)
任何抛开以上因素,光谈SQL中语法的性能分析的,都是耍流氓性能模拟如果不能在以上基础相同的情况下进行,就是没有参考价值的(一些答案并没能完整、准确复原题主的表情况及sql运行环境)相反,执行计划则可以很好体现事实上的执行情况,其自解释性也能帮助判断哪里出了问题Mysql的查询优化,必须具体情况具体分析,可以根据理论猜测、定位问题,但切勿盲目总结金科玉律(即便读了mysql源码,版本一变可能表现就不同了对吧)同样一条SQL,其运行环境的变化,会导致性能表现千差万别同样两条SQL的性能对比,其运行环境的变化,也会导致对比结果完全不同————————————我是傲娇的分割线--------------------------------------------
查询问题两步走:
1.explain先看执行计划。
2.SET profiling = 1;show profiles;看各步耗时。正常都是可以自助查出并解决问题。mysql的查询优化不能仅依赖数据集的规模进行调查。必须严格按我写的两点进行排查,研究执行计划才是靠谱的。
3.mysql5.6有一个新特性:
SET optimizer_trace=“enabled=on";
SELECT trace FROM information_schema.OPTIMIZER_TRACE INTO outfile ‘trace.json';
另外需要提到的一点就是explain中ken_len的计算问题:
key_len显示了条件检索子句需要的索引长度,但 ORDER BY、GROUP BY 子句用到的索引则不计入 key_len 统计值;
(1).索引字段的附加信息:可以分为变长和定长数据类型讨论,当索引字段为定长数据类型,比如char,int,datetime,需要有是否为空的标记,这个标记需要占用1个字节;对于变长数据类型,比如:varchar,除了是否为空的标记外,还需要有长度信息,需要占用2个字节;
(备注:当字段定义为非空的时候,是否为空的标记将不占用字节)
(2).同时还需要考虑表所使用的字符集,不同的字符集,gbk编码的为一个字符2个字节,utf8编码的一个字符3个字节;
MySQL中,key_len的计算规则如下:
如果列可以为空,则在数据类型占用字节的基础上加1,如int型,不能为空key_len为4,可以为空key_len为5
如果列是变长的,则在数据列所占字节的基数上再加2,如varbinary(10),不能为空,则key_len为10 + 2 ,可以为空则key_len为10+2+1
如果是字符型,则还需要考虑字符集,如某列的定义是varchar(10),且是utf8,不能为空,则key_len为10 * 3 + 2,可以为空则key_len为10*3+2+1
此外,decimal列的计算方法与上面一样,如果可以为空,则在数据类型占用字节的基础上加1,但是,decimal本身所占用字节数,计算就比较复杂。
根据官方文档可以知道,decimal定义为decimal(M,D),其中,M是总的位数,D是小数点后保留的位数。小数点前与小数点后的数字分开存储,且以9位数为1组,用4个字节保存,如果低于9位数,需要的字节数如下:
mysql如何处理死锁?
1. 当MySql发生死锁后,InnoDB将自动检测事务死锁,并立刻回滚,然后返回错误。回滚通常选择undo量最小(即:持有最少排他行级锁的)的事务。
2. MySql死锁涉及2个以上的事务,SHOW ENGINE INNODB STATUS返回的信息中LATEST DETECTED DEADLOCK部分所检测到最近的死锁信息仅显示最近的两个事务。
如何判断MySql死锁
除了应用日志,及在之前使用SHOW ENGINE INNODB STATUS命令外,我们可以利用binlog,slow log及general query log. 通过binlog,如果binlog_format=statement,每一个binlog事件都会有thread_id。只有提交过的事务才会被记录在binlog中,因此我们可以在binlog中查询Trx(2)。在例1中,我们知道什么时候发生死锁,也知道Trx(2)在9秒前启动。因此可以通过mysqlbinlog命令来找出语句位置。
如何在MySql中避免死锁
修改应用程序:某些情况下,通过将大的事务划分为若干小的事务可以减少死锁的频率,分割后锁会更快的释放。另外,死锁是在两个事务对同一数据集以不同的顺序操作时产生,要么是对同一张表要么是多个表。因此修改访问顺序,即串行化访问。当事务并发执行时最终发生锁等待而不是死锁。
修改表模式:例如删除外键,或者添加索引来减少扫描及锁定的行。
间隙锁:可以将事务的隔离级别该为提交读(read committed)以避免间隙锁。但binlog format应该修改为ROW或MIXED。