【问答】MySQL

MySQL的存储引擎,各自优缺点,MySQL引擎区别

InnoDB:适用于处理大数据,崩溃后可以利用事务日志很好地恢复;支持聚簇、B-Tree和全文索引,不支持Hash索引;支持数据缓存(访问数据时,表和索引数据会被缓存在缓冲池)、外键、行锁、事务和MVCC;可以混合查询InnoDB引擎的表和其他引擎的表;表文件:表名.frm(表结构文件)、表名.ibd(数据和索引文件);

MyISAM:查询效率很高,适合大量读场景;支持B-Tree和全文索引,不支持聚簇、Hash索引;不支持数据缓存、外键、事务、行锁(支持表锁);表文件:表名.frm(表结构文件)、表名.myd(数据文件)、表名.myi(索引文件);

其他引擎:ARCHIVE:档案,适用于存储大量独立的作为历史数据的数据,插入快,查询差;不支持索引,存储大小无限制,数据压缩较好,不支持delete和update;PERFORMANCE_SCHEMA:性能,专注收集MySQL运行性能数据;数据在内存中,重启后丢失;大多数表只读,用户不能创建;MEMORY:内存,存储临时不重要的数据,例如作为缓存,适合大量读的情形;数据在内存中,重启后丢失;CSV:数据存储在CSV文件;FEDERATED:联盟,适用于分布式;MRG_MYISAM:Merge多个相同定义的MyISAM表;BLACKHOLE:黑洞,像黑洞一样接受数据但不存储,适用于转发和过滤给Slave服务器;  

求表的Size,或做数据统计可用什么存储引擎

用MyISAM。MyISAM适合读多的场景:做很多count的计算,插入不频繁,查询非常频繁,没有事务,磁盘空间占用较少;InnoDB适合写多的场景:可靠性要求比较高,或者要求事务,表更新和查询都相当的频繁,并且表锁定的机会比较大的情况;

MyISAM查询速度要快于InnoDb是因为InnoDb查询时维护的东西要多些,InnoDb要缓存数据块,MyISAM只缓存索引块;InnoDb寻址要映射到块,再到行,MYISAM记录的直接是文件的OFFSET,定位比INNODB要快; INNODB还需要维护MVCC一致;

InnoDB对一行数据的读会加锁吗?

InnoDB根据事务隔离级别、是否主键、是否有索引、索引是否唯一等加锁不一样;RU下,读不加锁,写加行锁;RC下,快照读使用MVCC控制不加锁,但不管MVCC的版本号,当前读和写在主键或唯一索引上时锁行,非唯一索引锁查询到的所有记录,无索引锁所有行(会自动优化,先锁再判断不满足条件时自动释放);RR下,快照读使用MVCC控制不加锁,当前读和写在主键或唯一索引上时锁行,非唯一索引锁查询到的所有记录+间隙锁,无索引锁所有行+间隙锁;SR下,快照读自动升级为当前读,读加S锁,写加X锁;

MySQL的行级锁加在哪个位置

表级,直接锁定整张表,锁定期间其它进程无法对该表进行写操作,如果是写锁,则其它进程则读也不允许,MyISAM引擎默认;行级,仅对指定的记录进行加锁,这样其它进程还是可以对同一个表中的其它记录进行操作,InnoDB引擎默认;页级,表级锁速度快,但冲突多,行级冲突少,但速度慢,所以取了折衷的页级,一次锁定相邻的一组记录,BDB引擎默认(5.1版本后不直接支持,被Oracle收购);

MySQL锁,悲观锁、乐观锁、排它锁、共享锁、表级锁、行级锁

全局锁:给整个数据库实例加锁,如做全库逻辑备份的时候,命令:Flush tables with read lock(FTWRL);表级锁:包括表锁和元数据锁(meta data lock,MDL),命令:lock tables read/write;行锁:InnoDB引擎实现,分为X和S锁,只能作用在索引上,是通过给索引上的索引项加锁来实现的,不是对记录加锁,所以访问不同记录但是用的相同索引也可能锁冲突;间隙锁:锁索引前面的间隙,开区间如(3,5),间隙锁是不互斥的,RU和RC隔离级别没有间隙锁,RR用来解决幻读;临键锁:行锁+间隙锁,即左开右闭的区间如(3,5];共享锁S/排他锁X:都是行锁,共享锁用于并发读,排他锁用于更新删除(不包括插入),事务请求时会根据隔离级别和数据是否存在可能获取到行锁、间隙锁、临建锁之一;意向共享锁IS/意向排他锁IX:属于表锁,用于判断锁冲突;插入意向锁:特殊的间隙锁,锁住一行数据,用于并发插入;悲观锁:是由数据库自己实现的,要用的时候直接调用数据库的相关语句就可以了,如行锁、读锁和写锁等,都是在操作之前加锁,类似Java的sync;乐观锁,通过在表中增加version字段判断实现,类似Java的CAS;

乐观锁的业务场景及实现方式

乐观锁一般通过版本号实现,在表中增加一列,使用version或者时间戳表示版本号,每更新一次则版本号加1,更新数据时判断版本号是否相等;InnoDB的MVCC就是乐观锁的一种实现;乐观锁是先进行业务操作,不到万不得已不去拿锁,因此在进行完业务操作需要实际更新数据的最后一步再去拿一下锁就好,用于读多写少的场景;

数据库会死锁吗,举一个死锁的例子,MySQL怎么解决死锁

死锁产生原因:1. 事务之间对资源访问顺序的交替,如A事务锁住了A表再访问B表,B事务锁住了B表再访问A表;2. 多个事务并发修改同一记录,如事务A查询一条记录,有了S锁,再修改这条记录,想获取X锁,事务B正在修改这条记录,有了X锁,由于S锁存在,X锁释放不了;3. 索引不当导致全表扫描,由于升级为表锁,多个事务执行久了容易死锁;4. 间隙锁是不互斥的,如果两个事务查询不存在的记录获取到了同一个间隙锁,则插入数据时会死锁;解决方法:调整代码逻辑,优化SQl语句;

数据库隔离级别有哪些,各自的含义是什么,MySQL默认的隔离级别是是什么

事务并发问题:脏读:A读了B更新的数据,B回滚后是脏数据;不可重复读:A多次读取同一数据,B过程中更新并提交了数据,导致读取结果不一致;幻读:类似不可重复读,不可重复读侧重于修改,幻读侧重于新增或删除,导致少读或多读了数据;  

事务隔离级别:描述事务处理之间的相互影响。读未提交:A事务可读到B事务已更新未提交的数据,存在3个问题;读提交:A事务只能读到B事务已更新已提交的数据,只解决了脏读;可重复读:默认级别,A事务不能读到B事务已更新已提交的数据,使用MVCC加版本控制,select不更新版本号,读的历史版本数据是快照读,insert/update/delete会更新版本号是当前读,解决了脏读和不可重复读;串行化:会锁表,3个问题都解决了,但并发低; 

MySQL是如何实现事务的

数据库事务ACID概念:原子性:事务里的所有操作要么全部做完,要么都不做,事务成功的条件是事务里的所有操作都成功,只要有一个操作失败,整个事务就失败,需要回滚;一致性:数据库要一直处于一致的状态,事务的运行不会改变数据库原本的一致性约束;隔离性:并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响;持久性:事务提交后它所做的修改将会永久的保存在数据库上,即使出现宕机也不会丢失;

实现事务:原子性:通过undolog回滚数据实现,undolog记录的是数据的逻辑变化,为了在发生错误时回滚之前的操作,需要将之前的操作都记录下来,然后在发生错误时才可以回滚;持久性:通过redolog恢复数据实现,redolog由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redolog),前者是在内存中,后者在磁盘中,当事务提交之后会把所有修改信息都会存到该日志中; 隔离性:通过读写锁+MVCC实现读写分离,读读并行,读写并行;一致性:通过回滚,以及恢复,和在并发环境下的隔离做到一致性;

高并发下,如何做到安全的修改同一行数据。

使用悲观锁:数据库自带的悲观锁机制:共享锁、排他锁等;使用乐观锁:MVCC或者自己实现在表中新增version字段实现;使用缓存队列,将并发变成串行;

MVCC的含义,如何实现的

MVCC:多版本并发控制协议,大多数情况下代替行锁,通过MVCC+行锁提高并发,降低系统开销;实现原理:每行记录增加2个隐藏列:创建版本号和删除版本号(即事务ID);select:查询创建版本小于等于当前事务版本并且删除版本未定义或大于当前版本的行数据;delete:更新当前行的删除版本号为当前事务ID;insert:更新当前行的创建版本号为当前事务ID,删除版本号为undefined;update:先拷贝insert,再delete旧行;

MySQL性能优化方式

参数优化:wait-timeout(服务器关闭非交互连接之前等待活动的秒数)、interactive_timeout(服务器关闭交互式连接前等待活动的秒数)、max_connections(并发链接数)、max_user_connections(单用户最大连接数)、thread_concurrency(并发线程数CPU*2)、skip-name-resolve(禁止DNS解析,IP连接时关闭)、innodb_buffer_pool_size(缓存大小)、innodb_log_buffer_size (事务日志缓存大小)、query_cache_size(查询缓存大小);

执行计划:主要优化type访问类型;system:标志有一行记录,const:通过索引一次找到,eq_ref:唯一索引,ref:非唯一索引,可能多行,range:范围索引,index:全索引扫描,all:全表扫描;

分库分表:垂直拆分(按列拆,大表拆小表)、水平拆分(按行拆,大表拆小表);拆分策略:Hash取模、范围分片、地理位置分片、时间分片;问题:分布式事务、跨库Join、数据合并、横向扩容;分库分表中间件:mycat、Sharding-JDBC;

其他优化:服务器硬件、数据库配置、操作系统配置、索引优化、SQL优化、主从复制、读写分离等;    

读写分离何时强制要读主库,读哪个从库是通过什么方式决定的,从库的同步MySQL实现?

读写分离就是将数据库分为了一个主库和多个从库,主库写数据,从库读数据,主从库之间通过某种机制进行数据的同步,读写分离是用来解决数据库的读性能瓶颈的; 

MySQL实现:配置:主库开启binlog,设置server_id;从库设置server-id,通过change master语句指定主库位置,通过start slave开启从服务器同步;原理:主库的所有数据修改会写进binlog,生成一个log dump线程,用来给从库IO线程传binlog;从库生成两个线程:IO线程和SQL线程,实时监控主库binlog的变化,IO线程会请求主库的binlog,并将得到的binlog日志写到relay log(中继日志)文件中;SQL线程会读取relay log文件中的日志,并解析成具体操作,来实现主从的操作一致,从而最终数据一致; 

强制读主库的场景:主从服务器可能存在延迟,客户请求读从库数据可能读到旧数据,即过期读,此时需要将必须获取最新结果的请求强制读主库; 

读写分离的中间件可选择mycat/MySQL Router/MaxScale,从库的选择一般是通过轮询/随机的方式;  

对表做统计时可直接看schema info信息,即查看表的系统信息

MySQL中有一个名为information_schema的数据库,该库中有一个TABLES表,这个表主要字段分别是:TABLE_SCHEMA数据库名、TABLE_NAME表名、ENGINE所使用的存储引擎、TABLES_ROWS记录数、DATA_LENGTH数据大小、INDEX_LENGTH索引大小等,可通过该表查询表信息;

MySQL的binlog

binlog日志用于记录所有更新了数据或者已经潜在更新了数据(如没有匹配任何行的一个DELETE)的所有语句,语句以事件的形式保存,它描述数据更改;因为有了数据更新的binlog,所以可以用于实时备份,与master/slave复制,默认是关闭的;

MySQL记录binlog的方式主要包括三种模式?每种模式的优缺点是什么?

Row Level行模式:日志中会记录每一行被修改的数据;优点:可以不记录执行的sql语句的上下文相关的信息,仅仅只需要记录哪一条被修改,所以rowlevel的日志内容会非常清楚的记录下每一行数据修改的细节,不会出现某些特定的情况下的存储过程或function以及trigger的调用和触发无法被正确复制的问题;缺点:由于记录每一行被修改的数据所以会产生大量的日志内容

Statement Level SQL语句模式:默认,记录会修改数据的SQL语句,slave在复制的时候sql进程会解析成和原来master端执行过的相同的sql来再次执行;解决了row level的缺点,不需要记录每一行数据的变化,减少bin-log日志量,节约IO,提高性能;缺点:由于只记录语句,某些情况如使用了特定的函数或功能时会造成MySQL的复制出现问题; 

Mixed混合模式:根据具体执行的SQL自动选择行模式或者SQL模式,简单的SQL采用SQL模式,复杂的SQL采用行模式;

 查看日志:show binlog events或者使用mysqlbinlog工具;  

SQL的整个解析、执行过程原理

执行流程:1. 客户端发送SQL请求,服务端收到后新起线程处理,如果是Select并且未带SQL_NO_CACHE则进入查询缓存; 2. 查询缓存:查询缓存就是一个哈希表,将执行过的语句及其结果以键值对的格式缓存到内存中,其中key由查询SQL、数据库、客户端协议版本等生成,value就是查询结果,可使用SQL_NO_CACHE绕过缓存; 3. 解析器:包括词法解析:将SQL从左到右逐个字符输入,然后根据构词规则识别单词;语法解析:判断SQL语句是否满足Mysql语法,生成一颗语法树;4. 预处理器:查看SQL中列名是否正确和权限验证,获取对象锁;5. 优化器:对SQL语句进行优化达到最快的执行效果,生成一个执行计划交给执行器;6. 执行器:根据执行计划来进行执行查询,根据SQL的指令,逐条调用对应的底层存储引擎API逐步执行;若选择行在数据缓冲区,则直接读取返回,否则从数据库文件中读取,并放入缓冲区,再将查询结果返回给客户端;

说说什么是最左匹配?

最左匹配:最左优先,以最左边的为起点任何连续的索引都能匹配上,同时遇到范围查询(>、<、between、like)就会停止匹配,最左匹配原则都是针对联合索引来说的,只要查询条件中带有最左边的列,那么查询就会使用到索引;原理索引的底层是一颗B+树,联合索引也是,只不过联合索引的健值数量不是一个,而是多个,构建一颗B+树只能根据一个值来构建,因此数据库依据联合索引最左的字段来构建B+树

如何优化慢查询?

首先定位慢查询语句,打开配置slow_query_log、slow_query_log_file、long_query_time默认10秒;通过执行计划explain分析执行的SQL语句,查看索引使用情况,再优化SQL语句;还可以使用show profiles或者performance_schema查看SQL语句执行各个阶段的耗时情况,然后再针对性的优化比如缓存参数、磁盘IO等;

MySQL索引为什么用的是b+ tree而不是b tree、红黑树

B-树、B+树、红黑树,都是平衡查找树,从查询效率上讲平均都是O(logn),使用哪种数据结构是出于提高数据库的查询效率的考虑如减少磁盘IO次数,减少内存占用;B树每一层节点可以很多,层数比较少,红黑树层数较多,这就导致磁盘IO次数多,B-树每个节点都有数据,从而节点较大,内存和磁盘读取开销较大,B+树只有叶节点有数据,并且叶节点之间通过指针相连,可以范围查找,其他节点没数据所以节点较小,内存和磁盘IO读取较少;

分库分表如何选择分表键

垂直拆分:包括垂直分库(不同业务不同库)、垂直分表(按列拆,扩展字段单独的表);水平拆分:按行拆分,包括库内分表、分库分表,拆分策略有Hash取模、范围分片、地理位置分片、时间分片;分库分表中间件:MyCat、Sharding-JDBC; 

分库分表的问题:事务一致性问题:采用分布式事务XA协议或2PC解决,对一致性要求不高的系统可采用事务补偿达到最终一致性;跨节点关联查询问题:全局表每个数据库都保存一份,表增加冗余字段,在系统层面处理聚合和数据组装;跨节点分页、排序、函数问题:先在不同的节点中将数据进行排序并返回,然后将不同分片返回的结果集进行汇总和再次排序,最终返回给用户;全局主键问题:可采用UUID、专门的表维护ID、雪花算法生成分布式ID;

分库分表的情况下,查询时一般是如何做排序的?

如果查询语句中有分区字段或者根据分区规则可以定位到哪个分区表则直接查询对应的分区表进行排序;如果必须从多个分区表查询数据,如查询前N条数据,则从各个分区表各自排序查询前N条数据,再在程序中进行汇总排序取前N条数据返回;

SQL优化的一般步骤是什么,怎么看执行计划,如何理解其中各个字段的含义。

1. 使用show status查询数据库的运行状况:查询CRUD次数:SHOW STATUS LIKE 'com_select%'、SHOW STATUS LIKE 'com_insert%'; 

2. 定位慢查询SQL:开启慢查询slow-query-log,设置慢查询时间long_query_time,设置慢查询日志路径slow_query_log_file,如果日志较多可使用mysqldumpslow工具分类汇总; 

3. 执行计划分析SQL:explain SQL,各字段含义:id:查询序列号,id越大越先执行,相同的id从上到下执行;select_type:查询类型:simple(简单查询),primary(主查询即外层查询),subquery(子查询),derived(from后的子查询),union(union后的查询),union result(获取UNION最后结果的查询);table:查询的表;type:找到匹配行用到的访问类型,NULL(不用访问表或索引就可直接得出结果),system(仅有一行的系统表),const(最多只有一个匹配行如主键或唯一索引查询),eq_ref(唯一索引,多表查询的关联条件),ref(非唯一索引,或唯一索引的前缀扫描),range(检索指定范围的行,使用一个索引来选择行),index(索引全扫描),ALL(全表扫描);possible_keys:查询时可能使用到的索引;key:表示实际使用的索引;key_len:使用到的索引字段的长度;ref:显示该表的索引字段关联了哪张表的哪个字段;rows:扫描行的数量;Extra:执行情况的说明和描述。包含不适合在其它列中显示但对执行计划非常重要的额外信息; 

4. 使用show profile分析sql:查询是否支持:SELECT @@have_profiling;开启profiles:set profiling=1;查看所有即SQL ID:show profiles;查看指定SQL ID的具体执行阶段的耗时:show profile for query ID;    

5. 通过trace分析优化器如何选择执行计划:打开trace并使用json格式:SET OPTIMIZER_TRACE="enabled=on",END_MARKERS_IN_JSON=on;查看trace:SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE; 

MySQL的索引原理,索引的类型有哪些,如何创建合理的索引,索引如何优化。

索引原理:MySQL中索引是由存储引擎实现的,不同存储引擎实现不一样;MyISAM引擎是非聚簇索引,使用B+Tree作为索引结构,可以没有主键,非叶节点存放索引值,叶节点存放索引值和数据地址,每个索引都是一颗B+树,索引和数据是分开存放的;InnoDb是聚簇索引,使用B+Tree作为索引结构,必须要有主键,但是索引和数据是存放在一起的即主索引就是表数据文件,主键搜索很高效,但辅助索引需要检索两遍:先根据辅助索引检索到主键,再根据主键检索到数据;

索引类型:按列属性分类:主键索引(不可重复,不可为空)、唯一索引(不可重复,可以空)、普通索引(无限制)、全文索引(对大文本text、blob的索引)、空间索引(GIS数据)、组合索引(对多列建立的索引,遵循最左前缀原则,ABC索引相当于建了A、AB、ABC3个索引);按数据结构分类:哈希索引:只有内存存储引擎才支持;实现原理:根据索引列的值计算hashcode,在hashcode的位置存储该行数据的物理位置实现;一个值对应一个hashcode,所以不支持排序和范围查询;BTree索引:每个节点包含索引键和数据,可以使用二分查找;B+Tree索引:数据都在叶子节点,其他节点只有索引键,磁盘读写代价更低;按存储结构分类:聚簇索引:叶子节点保存索引和数据,顺序就是数据的物理顺序;非聚簇索引:叶子节点保存索引和主键或者数据地址;

索引使用:什么时候用:主键自动建索引,查询条件,排序,外键,关联字段应该建索引;什么时候不用:经常增删改的列,大量重复的列,记录太少的表;索引失效情况:组合索引的NULL列、like %xx%、索引列有函数和表达式、使用不等于(<>!=,但主键和整型不会失效)、IS (NOT) NULL、字段类型不一致(包括字符串不加单引号)、OR(非每个条件都有索引时);索引优化:最左前缀、模糊查询优化、全文索引、短索引(对前N个字符建索引);

聚集索引和非聚集索引的区别

聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个;聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续;聚集索引:物理存储按照索引排序;聚集索引是一种索引组织形式,索引的键值逻辑顺序决定了表数据行的物理存储顺序;非聚集索引:物理存储不按照索引排序;非聚集索引则就是普通索引了,仅仅只是对数据列创建相应的索引,不影响整个表的物理存储顺序;都是通过二叉树的数据结构来描述的,聚簇索引:索引的叶节点就是数据节点,而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块;

某个表有近千万数据,CRUD比较慢,如何优化

数据千万级别之多,占用的存储空间也比较大,可想而知它不会存储在一块连续的物理空间上,而是链式存储在多个碎片的物理空间上,可能对于长字符串的比较,就用更多的时间查找与比较,这就导致用更多的时间。可以做表拆分,减少单表字段数量,优化表结构,主要两种拆分垂直拆分和水平拆分;

MySQL怎么优化table scan的

避免全表扫描首先应考虑在where及order by涉及的列上建立索引,会放弃使用索引的情况:使用Null判断条件、列中包含null值(可设置默认值为0)、使用 !=或<>操作符(<,<=,=,>,>=,BETWEEN,in不会) 、like ‘%xx%’、使用exist代替in、使用or连接条件(使用union代替)、Where语句中含有表达式等;

如何写sql能够有效的使用到复合索引

查询字段顺序需要和索引中字段顺序保持一致

MySQL中in 和exists 区别

exists对外表用loop逐条查询,每次查询都会查看exists的条件语句,如果外表A有n条记录,那么exists查询就是将这n条记录逐条取出,然后判断n遍exists条件;in查询相当于多个or条件的叠加,是在内存里遍历比较,子条件返回结果必须只有一个字段,exists就没有这个限制;所以外表和内表数据量差不多时2者性能差不多,IN适合于外表大而子查询表小的情况,EXISTS适合于外表小而子查询表大的情况;

数据库自增主键可能的问题

单表使用没有问题,如果分库分表、或者涉及到数据迁移,由于ID可能重复就会存在问题;解决:使用单独的表记录生成的ID;采用UUID;分布式ID采用雪花算法生成;采用时间戳+随机数;

分库分表中间件比如Sharding-JDBC等他们的原理知道么

相比于Spring基于AbstractRoutingDataSource实现分库分表Sharding-JDBC兼容性更好;Sharding-JDBC是基于JDBC实现,封装了SpringShardingDataSource、ShardingConnection和ShardingPreparedStatement实例对象,执行executeQuery()时ShardingPreparedStatement会根据逻辑SQL经过分库分表策略逻辑计算获得分库分表的路由结果SQLRouteResult,SQLRouteResult中包含真实的数据源以及转换后的真正SQL,利用真实的数据源去执行获得ResultSet,将ResultSet列表封装成一个可以顺序读的ResultSet对象IteratorReducerResultSet;

MySQL的主从延迟怎么解决

主从延迟是由于主库同步数据到从库时由于从库繁忙或者其他原因导致的同步不及时,从而从库查询不到数据;解决方案:降低延迟时间:优化从库配置,升级MySQL版本(5.6前单线程复制,之后多线程),增加从库节点降低从库压力;根本解决方案:写入数据时判断从库完成同步后再返回成功;使用redis缓存存在延迟的数据,完成同步后再删除缓存;存在延迟的读改成读主库;

你可能感兴趣的:(【问答】MySQL)