一、MySQL架构
锁
什么是锁?
当多个连接并发地存取MySQL数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。
加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作。
锁的分类?
按照粒度分类:
- 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
- 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
按照功能分类:
- 共享锁(读锁):其他事务可以并发读取数据,阻止一切修改操作,如果事务对共享锁进行修改。
- 排它锁(写锁):某个事务对某行数据加了排它锁,只有该事务能对其进行读和写操作,其他事务不能对其加任和锁。其他进程不能读取,也不能进行写操作。排它锁是悲观锁的一种实现方式。排它锁或阻塞其他的排它锁和共享锁。
- 全局锁:使用场景比如做全库逻辑备份,该命令可以使整个库处于只读状态,阻止一切修改操作。
按照逻辑分类:
- 乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。
- 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。
什么是死锁?怎么解决?
死锁是指两个或者两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象。
如何解决:(1)设置超时时间;(2)发起死锁检测,发现死锁后,主动回滚死锁中的某一个事务,让其他事务继续执行。
乐观锁和悲观锁的实现方式?
悲观锁是通过加锁实现,比如MySQL的行锁和表锁等;乐观锁是通过版本号或者CAS算法实现的:
版本号:在数据中增加一个字段version,表示该数据的版本号,每当数据被修改,版本号加1。当某个线程查询数据时,将该数据的版本号一起查出来;当该线程更新数据时,判断当前版本号与之前读取的版本号是否一致,如果一致才进行操作。需要注意的是,这里使用了版本号作为判断数据变化的标记,实际上可以根据实际情况选用其他能够标记数据版本的字段,如时间戳等。
CAS算法:CAS3个操作数:需要读写的内存位置(V)、进行比较的预期值(A)、拟写入的新值(B)。操作逻辑如下:如果内存位置V的值等于预期的A值,则将该位置更新为新值B,否则不进行任何操作。许多CAS的操作是自旋的:如果操作不成功,会一直重试,直到操作成功为止。
这里引出一个新的问题,既然CAS包含了Compare和Swap两个操作,它又如何保证原子性呢?答案是:CAS是由CPU支持的原子操作,其原子性是在硬件层面进行保证的。
乐观锁和悲观锁优缺点和适用场景?
二者并无优劣之分。适用场景不同而已。
功能限制:1.CAS只能保证单个变量操作的原子性,无法保证多个变量操作的原子性;2.版本号机制,如果query的时候是针对表1,而update的时候是针对表2,也很难通过简单的版本号来实现乐观锁。
竞争激烈程度:当竞争不激烈 (出现并发冲突的概率小)时,乐观锁更有优势;当竞争激烈(出现并发冲突的概率大)时,悲观锁更有优势。
隔离级别与锁的关系?
- 在读未提交级别下,读取数据不需要加共享锁,这样就不会跟被修改的数据上的排他锁冲突。
- 在读已提交级别下,读操作需要加共享锁,但是在语句执行完以后释放共享锁。释放完成后,如果其他事务对他修改提交后,再次读取的值和前面读取的值不一样,这就是不可重复读。
- 在可重复读级别下,读操作需要加共享锁,但是在事务提交之前并不释放共享锁,也就是必须等待事务执行完毕以后才释放共享锁。对于范围查询只会锁住当前数据库已经存在符合条件的行,如果新增一个符合条件的行,再次范围查询的结果就和上次范围查询的结果不同,这就是脏读。
- 序列化是限制性最强的隔离级别,因为该级别锁定整个范围的键,并一直持有锁,直到事务完成。
不同引擎的锁机制有什么不同?
- MyISAM在执行查询(SELECT)前,会自动给涉及的所有表加读,在执行(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁。
- InnoDB的每条语句会包装在一个事务(包含一条或者多条语句)中,采用两阶段锁定协议:在事务执行过程中,对执行的每一个语句涉及的行隐式加锁,在提交或者回滚时一起释放。当然也可以通过select * from table lock in share mode或者select … for update显示加锁。
事务
什么是事务?
事务就是一个最小的不可再分的工作单元;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元),一个事务可以包含多个动作,这些动作要么全部成功,要么全部失败。
ACID分别是什么?
- A:原子性(atomicity)一个事务必须被视为一个不可分割的工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚。对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。
- C:一致性(consistency)数据库总是从一个一致性状态转换到下一个一致性状态。在前面的例子中,一致性确保了,即使在执行第3、4条语句之间时系统崩溃,支票账户中也不会损失200美元。如果事务最终没有提交,该事务所做的任何修改都不会被保存到数据库中。
- I:隔离性(isolation)通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的,这就是隔离性带来的结果。在前面的例子中,当执行完第3条语句、第4条语句还未开始时,此时有另外一个账户汇总程序开始运行,其看到的支票账户的余额并没有被减去200美元。后面我们讨论隔离级别(isolation level)的时候,会发现为什么我们要说“通常来说”是不可见的。
- D:持久性(durability)一旦提交,事务所做的修改就会被永久保存到数据库中。此时即使系统崩溃,数据也不会丢失。
ACID各自的实现原理是什么?
- 原子性的实现依赖undo log回滚日志。每一条数据变更(insert/update/delete)操作都伴随一条undo log的生成,并且回滚日志必须先于数据持久化到磁盘上。所谓的回滚就是根据回滚日志做逆向操作,比如delete的逆向操作为insert,insert的逆向操作为delete,update的逆向为update等。
- 数据库的一致性依赖于其他三种特性:原子性,隔离性,持久性。所谓一致性,指的是数据处于一种有意义的状态,例如从帐户A转一笔钱到帐户B上,A账户上减少的钱数等于B账户上增加的钱数才是一种有意义的状态。
- 隔离性通过锁机制来实现。使用锁机制,保证每个事务能够看到的数据总是一致的,就好像其他事务不存在一样,多个事务并发执行后的状态和它们串行执行后的状态是等价的。原则上一般是两种类型的锁:乐观锁和悲观锁。
- 持久性通过redo log来保证。Mysql是先把磁盘上的数据加载到内存中,在内存中对数据进行修改,再刷回磁盘上。如果此时突然宕机,内存中的数据就会丢失。但是如果事务提交前直接把数据写入磁盘太浪费IO资源,因此使用redo log来解决持久性和读写IO消耗严重之间的平衡问题,当做数据修改的时候,不仅在内存中操作,还会在redo log中记录这次操作。当事务提交的时候,会将redo log日志进行刷盘(redo log一部分在内存一部分在磁盘)。当数据库宕机重启会将redolog中的内容恢复到数据库再根据undo log和binlog内容决定回滚数据还是提交数据。
事务的隔离级别有哪些?
- READ UNCOMMITTED(未提交读)在READ UNCOMMITTED级别,在事务中可以查看其他事务中还没有提交的修改。读取未提交的数据,也称为脏读(dirty read)。
- READ COMMITTED(提交读)大多数数据库系统的默认隔离级别是READ COMMITTED(但MySQL不是)。READ COMMITTED满足前面提到的隔离性的简单定义:一个事务可以看到其他事务在它开始之后提交的修改,但在该事务提交之前,其所做的任何修改对其他事务都是不可见的。这个级别仍然允许不可重复读(nonrepeatable read),这意味着同一事务中两次执行相同语句,可能会看到不同的数据结果
- REPEATABLE READ(可重复读)解决了不可重复读问题,保证了在同一个事务中多次读取相同行数据的结果是一样的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读(phantom read)的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(phantom row)。InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)解决了幻读的问题。REPEATABLE READ是MySQL默认的事务隔离级别。
- SERIALIZABLE(可串行化)SERIALIZABLE是最高的隔离级别。该级别通过强制事务按序执行,使不同事务之间不可能产生冲突,从而解决了前面说的幻读问题。简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。实际应用中很少用到这个隔离级别,除非需要严格确保数据安全且可以接受并发性能下降的结果。
Mysql的事务的实现原理?
事务是通过redo日志和innodb的存储引擎日志缓冲(Innodb log buffer)来实现的。 当开始一个事务的时候,会记录该事务的lsn(log sequence number)号; 当事务执行时,会往InnoDB存储引擎的日志的日志缓存里面插入事务日志; 当事务提交时,必须将存储引擎的日志缓冲写入磁盘(通过innodb_flush_log_at_trx_commit来控制)。 也就是写数据前,需要先写日志。这种方式称为“预写日志方式”(waf)。
什么是快照读和当前读?
MVCC并发控制中,读操作可以分成两类:快照读 (snapshot read)与当前读 (current read)。
快照读:读取的是记录的可见版本 (有可能是历史版本),不用加锁。
当前读:读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。
什么是MVCC?
加锁是一种控制并发的方式,但是加锁毕竟是一个比较消耗资源的操作,因此MySQL也实现了MVCC(Multi-Version Concurrency Control ),核心思想是给 每一条数据加上两个版本号,一个是当前的数据版本号,一个是该数据的删除版本号。每开启一个一个新的事务,系统版本号自动递增。。通过版本的控制,在一定程度上尚避免加锁也可以实现并发控制。
在MySQL中,MVCC的大致工作原理如下:
select 查询语句只会获取取符合下面两个条件的数据:①数据版本号小于等于当前事务的版本号。这样可以保证查到的数据要么是之前就存在的,要么是本事务操作的。②数据的删除版本号要么为空,要么大于事务当前的版本号,这样可以保证在此事务之前,该行数据没有被删除。
insert 插入数据,将数据的版本号设置为当前事务的版本号。
delete 删除数据,将删除行的删除版本号设置为当前事务的版本号。
update 对原数据进行删除操作,然后插入新数据,所以相当于上面两个操作的合集。
引擎
这里主要将MyIsam引擎和InnoDB引擎。
MyISAM和InnoDB的区别?
- InnoDB支持事务,但是MyISAM不支持事务。
- InnoDB支持外键,但是MyISAM不支持外键。
- InnoDB最小粒度是行锁,MyISAM是表锁。
- InnoDB是聚簇索引,必须要有主键,通过辅助索引需要查询两次,先查询到主键,再通过主键索引查询到数据,且主键不能过大,主键过大,索引也会很大;MyISAM是非聚簇索引,数据和索引是分离的,索引保存的是数据文件的指针。
- InnoDB不保存表的具体行数,count()需要全表扫描;MyISAM会保存表的行数,查询count()会很快。
- InnoDB适合场景:需要事务支持、行锁、对高并发有很好地适应能力,但需要确保查询是通过索引完成、数据更新较为频繁。MyISAM适合场景:不需要事务支持、并发相对较低、数据修改相对较少、以读为主、数据一致性要求不是特别高。
MyISAM索引与InnoDB索引的区别?
- InnoDB是聚集索引,数据文件是和(主键)索引绑在一起的,通过主键索引到整个记录,必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,因为辅助索引是以建索引的字段为关键字索引到主键,所以需要两次,先查询到主键,然后再通过主键查询到数据。
- MyISAM是非聚集索引,也是使用B+Tree作为索引结构,索引和数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
也就是说:InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。
InnoDB引擎的4大特性?
- 插入缓冲 (Insert Buffer/Change Buffer):插入缓存之前版本叫insert buffer,现版本 change buffer,主要提升插入性能,change buffer是insert buffer的加强,insert buffer只针对insert有效,change buffering对insert、delete、update(delete+insert)、purge都有效。
- 双写机制(Double Write):在InnoDB将BP中的Dirty Page刷(flush)到磁盘上时,首先会将(memcpy函数)Page刷到InnoDB tablespace的一个区域中,我们称该区域为Double write Buffer(大小为2MB,每次写入1MB,128个页,每个页16k,其中120个页为后台线程的批量刷Dirty Page,还有8个也是为了前台起的sigle Page Flash线程,用户可以主动请求,并且能迅速的提供空余的空间)。在向Double write Buffer写入成功后,第二步、再将数据分别刷到一个共享空间和真正应该存在的位置。
- 自适应哈希索引(Adaptive Hash Index,AHI):哈希算法是一种非常快的查找方法,在一般情况(没有发生hash冲突)下这种查找的时间复杂度为O(1)。InnoDB存储引擎会监控对表上辅助索引页的查询。如果观察到建立hash索引可以提升性能,就会在缓冲池建立hash索引,称之为自适应哈希索引(Adaptive Hash Index,AHI)。
- 预读 (Read Ahead):预读操作是一种IO操作,用于异步将磁盘的页读取到buffer pool中,预料这些页会马上被读取到。预读请求的所有页集中在一个范围内。
索引
什么是索引?
索引是存储引擎用于快速找到记录的的一种数据结构。
索引的实现原理?
索引目的是通过不断地缩小查找目的数据的范围来筛选出目的数据,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。数据库就是把数据分成若干段,精确定位查找范围,去除无效数据,提高查找速度。
索引的优缺点?
优点:
- 索引大大减少服务器需要扫描的数据量。
- 索引帮助服务器避免排序和临时表。
- 索引将随机IO变为顺序IO。
缺点:
- 建立索引需要占用物理空间。
- 会降低表的增删改的效率,因为每次对表记录进行增删改,需要进行动态维护索引,导致增删改时间变长。
索引的分类?
- B-tree索引(包含b+tree索引)
- 主键索引:
- Innodb主键索引:primary key :加速查找+约束(不为空且唯一)
- MyIsam主键索引和平台索引结构上差不多,但是约束上加了唯一性。
- 辅助索引:
- 普通索引index :加速查找
- 唯一索引unique:加速查找+约束 (唯一)
- 联合索引
- -primary key(id,name):联合主键索引
- -unique(id,name):联合唯一索引
- -index(id,name):联合普通索引
- 哈希索引
哈希索引是基于哈希表实现的,只有精确匹配索引所有列的查询才有效。对于每一行数据存储引擎都会对所有的所有列计算一个哈希码。哈希索引将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。
在MySQL中,自有Memory引擎显式支持哈希索引。哈希索引只存储对于的哈希值,所以索引的结构十分紧凑,这也让哈希索引的查找速度非常快。
哈希索引的缺点:
- 哈希索引不包含行数据,查找过程先找到数据行指针,再去磁盘中读取查找数据。
- 哈希索引不是按照所有值顺序排序存储的,所以无法用于排序。
- 哈希索引只能全值匹配,不支持部分索引列匹配查找。
- 哈希索引只支持等值比较查询,如:=、IN()、<=>
- 哈希冲突很多的话,一些索引维护操作的代价也会很高。
InnoDB引擎有一种特殊的公共:自适应哈希索引,对永无无感知。
- 全文索引
- 全文索引fulltext :用于搜索很长一篇文章的时候,效果最好。全文搜索的索引。只能用于InnoDB或MyISAM表,只能为CHAR、VARCHAR、TEXT列创建。
- 空间数据索引
- 空间索引spatial :MyISAM表支持空间索引,用作地理数据存储。空间索引会冲所有维度来索引数据,查询时,可以有效的使用任意比较索引中的值。
B-Tree、B+Tree以及MySQL的B+Tree的区别?
B树的特点:
- 每个节点(不区分内节点和叶子节点)都存储key和data。
- 任何关键出现且只出现在一个节点中。
- 搜索有可能在非叶子节点结束
- 在关键字全集内做一次查找,性能逼近二分查找算法
B+Tree的特点:
- 非叶子节点只存储键值信息,不存放数据。
- 数据记录都存放在叶子节点中。
- 所有叶子节点之间都有一个链指针(普通B+Tree增加的是单向指针,MySQL增加的是双向指针)
为什么mysql的索引使用B+树而不是B树呢?
- B+树更适合外部存储(一般指磁盘存储),由于内节点(非叶子节点)不存储data,所以一个节点可以存储更多的内节点,每个节点能索引的范围更大更精确。也就是说使用B+树单次磁盘IO的信息量相比较B树更大,IO效率更高。
- mysql是关系型数据库,经常会按照区间来访问某个索引列,B+树的叶子节点间按顺序建立了链指针,加强了区间访问性,所以B+树对索引列上的区间范围查询很友好。而B树每个节点的key和data在一起,无法进行区间查找。
InnoDB的一棵B+树可以存放多少行数据?
根节点最大数据条数:(16K)16384/14(bigint8个字节+指针6个字节) = 1170个单元
叶子节点:(16K)/1k(假设每条数据1K) = 16条数据
2层B+树:1170* 16
3层B+树:1170117016
如何创建高性能索引?
- 创建前缀压缩索引,缩小索引块大小,提高查询速度。使用前缀索引时,要选择合适的前缀值长度:
- 索引的选择性:不重复的索引值 / 表数据总数 的值。选这个值约小,择性越高,反之越低。
- 对于BLOB和TEXT或者很长的VARCHAR类型的列,必须使用前缀索引。
- 当完整索引列的前n个字符的选择性和完整索引列的选择性相差值比n-1和n+1都小时,前缀索引的前缀字符数就为n
- 合适的情况可以创建多列索引。且一般将选择性最高的列放在索引的最前列,但有时候也需要把选择性小的列放前面,查询的时候如果要命中索引的话就必须IN选择性小的索引,比如:肤色,in(white,yellow,black)
- 充分利用索引覆盖机制提高查询效率。
- 尽量使用索引扫描来做排序,当索引的列顺序和order by子句顺序完全一致,且所有列的排序方向一样,MySQL才能使用索引来对结果做排序。
- 避免冗余和重复索引,删除未用到的索引。
什么是索引下推?
索引下推(index condition pushdown )简称ICP,在Mysql5.6的版本上推出,用于优化查询。在不使用ICP的情况下,在使用非主键索引(又叫普通索引或者二级索引)进行查询时,存储引擎通过索引检索到数据,然后返回给MySQL服务器,服务器然后判断数据是否符合条件 。在使用ICP的情况下,如果存在某些被索引的列的判断条件时,MySQL服务器将这一部分判断条件传递给存储引擎,然后由存储引擎通过判断索引是否符合MySQL服务器传递的条件,只有当索引符合条件时才会将数据检索出来返回给MySQL服务器 。索引条件下推优化可以减少存储引擎查询基础表的次数,也可以减少MySQL服务器从存储引擎接收数据的次数。
如何有效维护索引和库表?
- 使用CHECK TABLE检查是否发生表损坏,然后使用REPAIR TABLE来修复表。
- 使用ANALYZE TABLE来重新生成统计信息,从而更新索引的统计信息。使用show index from table_name查看索引信息。
- 减少索引和数据碎片。可以通过OPTIMIZE TABLE指令或者重新导出和导入来整理数据。或者通过no-op的alter table操作来重建表,比如alter table table_name engine=(之前的engine)
索引失效场景有哪些?
- 组合索引未使用最左前缀,例如组合索引(age,name),where name='张三’不会使用索引;
- OR会使索引失效。如果查询字段相同,也可以使用索引。例如where age=20 or age=30(索引生效),where age=20 or name=‘张三’(这里就算你age和name都单独建索引,还是一样失效);
- 类型不一致导致的索引失效。例如where name=张三(索引失效),改成where name=‘张三’(索引有效);
- like未使用最左前缀,where A like ‘%China’;
- 在索引列上做任何操作计算、函数,会导致索引失效而转向全表扫描;
- 如果mysql估计使用全表扫描要比使用索引快,则不使用索引;
索引的设计原则?
- 索引列的区分度越高,索引的效果越好。比如使用性别这种区分度很低的列作为索引,效果就会很差。
- 尽量使用短索引,对于较长的字符串进行索引时应该指定一个较短的前缀长度,因为较小的索引涉及到的磁盘I/O较少,查询速度更快。
- 索引不是越多越好,每个索引都需要额外的物理空间,维护也需要花费时间。
- 利用最左前缀原则。
二、Schema和数据类型优化
如何正确选择列数据类型?
- 尽量使用占用空间小的数据类型
- 尽量使用简单的数据类型(整形 > data,time > enum char > varchar > blob, text)
- 尽量避免NULL
- 尽量使用timestamp和datetime来存储时间数据。
如何正确设计数据库schema?
- 为每个字段选择合适的字段类型。
- 字段个数不宜太长,32个左右刚刚好。
- 避免太多的关联(外键)。
- 字段定义尽量避免NULL。
- 适当的字段冗余有助于特定场景的查询和排序。
什么是三范式?
◆ 第一范式(1NF):强调的是列的原子性,即列不能够再分成其他几列。
◆ 第二范式(2NF):首先是 1NF,另外包含两部分内容,一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。
◆ 第三范式(3NF):首先是 2NF,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况。
范式有哪些优缺点?
优点:
- 范式化的更新操作通常比反范式化要快。
- 更新操作,修改的数据更小更高效。
- 范式的表通常更小,可以更好的放在内存中,执行操作更快。
缺点:
- 查询需要关联,影响查询效率。
反范式有哪些优缺点?
优点:因为数据几种在一个表上,查询数据不需要关联查询,或者更少的关联查询。
缺点:插入和更新操作影响数据行多,容易造成数据不一致。
三、查询性能优化
MySQL优化有哪些方向?
- SQL语句优化:
- a、尽量不要使用select(*)
- b、where语句的通配符尽量不要放在前面,且最好不要给字段加运算。
- c、尽量不要使用limit,因为limit的操作是在内存中执行的,实际上IO以及查询出来所有复合条件的数据。
- d、尽量少用多表连接,将复杂的sql拆分多次执行。
- 索引优化:
- a、对WHERE子句中频繁使用的建立索引;
- b、尽可能使用唯一索引,重复值越少,索引效果越强;
- c、尽量使用短索引,如果char(255)太大,应该给它指定一个前缀长度,大部分情况下前10位或20位值基本是唯一的,那么就不要对整个列进行索引;
- d、充分利用左前缀,对WHERE语句如果有AND并列,只能识别一个索引(获取记录最少的那个),这时可以将AND的字段加复合索引;
- e、不要过度索引。索引越多,占用空间越大,反而性能变慢;
- f、应尽量使用到索引覆盖,避免回表。
- 数据库表结构优化:
- a、尽可能选择小的数据类型:比如ip用int而不是varchar,金额使用decimal或者int,char代替varchar。
- b、尽可能使用not null:非空比空处理效率快,空会额外增加一个字节的空间。
- c、单表尽量少于30个字段。
- d、innodb擅长更新和删除,同时也支持事务,外键等高级功能。
- e、myisam擅长查询及插入。
- 库表水平或者垂直拆分。
- 建立中间表。
- 系统配置优化。
- 开启缓存:query_cache_type设置为ON
- 硬件优化。
简述MySQL的分表?
垂直分表:垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。 垂直分割一般用于拆分多字段和访问频率低的字段,分离冷热数据。适用于记录不是非常多的,但是字段却很多,这 样占用空间比较大,检索时需要执行大量的I/O,严重降低了性能,这个时候需要把大的自读那拆分到另一个表中,并且该表与源表时一对一关系。
水平分表:水平拆分是指数据表行的拆分,表的行数超过500万行或者单表容量超过10GB时,查询就会变慢,这时可以把一张的表的数据拆成多张表来存放。水平分表尽可能使每张表的数据量相当,比较均匀。
水平分表的标准:用户表可以根据用户的手机号段进行分割如user183、user150、user153、user189等,每个号段就是一张表 用户表也可以根据用户的id进行分割,加入分3张表user0,user1,user2,如果用户的id%3=0就查询user0表,如果用户的id%3=1就查询user1表 对于订单表可以按照订单的时间进行分表。
简述MySQL的分区?
分区是根据一定的规则,数据库把一个表分解成多个更小的、更容易管理的部分。就访问数据库应用而言,逻辑上就只有一个表或者一个索引,但实际上这个表 可能有N个物理分区对象组成,每个分区都是一个独立的对象,可以独立处理,可以作为表的一部分进行处理。分区对应用来说是完全透明的,不影响应用的业务逻辑。
分区有利于管理非常大的表,它采用分而治之的逻辑,分区引入了分区键的概念,分区键用于根据某个区间值(或者范围值)、特定值列表或者hash函数值执行数据的聚集, 让数据根据规则分布在不同的分区中,让一个大对象碧昂城一些小对象。MySQL分区即可以对数据进行分区也可以对索引进行分区。
分区类型:
- range分区:基于一个给定的连续区间范围(区间要求连续并且不能重叠),把数据分配到不同的分区
- list分区:类似于range分区,区别在于list分区是居于枚举出的值列表分区,range是基于给定的连续区间范围分区
- hash分区:基于给定的分区个数,把数据分配到不同的分区
- key分区:类似于hash分区
慢查询的原因有哪些?
- SQL 没加索引
- SQL 索引不生效
- Limit 深分页问题
- 单表数据量太大
- Join 或者子查询过多
- in 元素过多
- order by使用了文件排序
- 锁等待时间长
SQL语句优化有哪些方向?
1、外连接比子查询效率高
2、union all 要比 union 效率高
3、用 PreparedStatement 一般来说比 Statement 性能高:一个 sql 发给服务器去执行,涉及步骤:语法检查、语义分析,编译,缓存
4、加索引,但每个表不要加多了 ,对where/ order by 的前几个字段加,必要时加组合索引
5、主键推荐数字类型
6、不要过多的使用sql中的函数 譬如avg()、sum()、substring() 等等
7、应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描
8、in 和 not in 、like也要慎用,否则会导致全表扫描
9、任何地方都不要使用 select *
10、避免频繁创建和删除临时表,以减少系统表资源的消耗
11、不要在查询的前面更新 ,同一个事务下如果更新操作耗时漫长,会导致查询也漫长,从而导致服务垮掉。
12、不要使用触发器,如果数据量大了会超慢
13、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,可以空值设为0 ,查询时查等于0的就行了。
14、程序中的sql 要防止sql注入的风险,要用占位符?,主键id设置成雪花算法那种,避免涉密数据被别人从网页篡改替换别的有序id从而查到数据。
四、其他
MySQL中数据在磁盘中数据的存储是什么样的?
了解MySQL的几种连接查询吗?
- 外连接
外连接主要分为左外连接(LEFT JOIN)、右外连接(RIGHT JOIN)、全外连接(MySQL不支持)。
左外连接:显示左表中所有的数据及右表中符合条件的数据,右表中不符合条件的数据为null。
右外连接:显示左表中所有的数据及右表中符合条件的数据,右表中不符合条件的数据为null。
- 内连接:只显示符合条件的数据
- 交叉连接:使用笛卡尔积的一种连接。
笛卡尔积,百度百科的解释:两个集合_X_和_Y_的笛卡尔积表示为_X_ × Y,第一个对象是_X_的成员而第二个对象是_Y_的所有可能有序对的其中一个成员 。例如:A={a,b},B={0,1,2},A × B = {(a,0),(a,1),(a,2),(b,0),(b,1),(b,2)}
MySQL的复制原理以及流程?
- 基本原理流程,3个线程以及之间的关联:
1.binlog输出线程:每当有从库连接到主库的时候,主库都会创建一个线程然后发送binlog内容到从库。在从库里,当复制开始的时候,从库就会创建两个线程进行处理: 2.从库I/O线程:当START SLAVE语句在从库开始执行之后,从库创建一个I/O线程,该线程连接到主库并请求主库发送binlog日志更新到从库的中继日志relay log文件上。 3.从库的SQL线程:负责读取中继日志,解析出主服务器已经执行的数据更改并在从服务器中重放(Replay)。
- 具体步骤
步骤一:主库db的更新事件(update、insert、delete)被写到binlog
步骤二:从库发起连接,连接到主库
步骤三:此时主库创建一个binlog输出线程,把binlog的内容发送到从库
步骤四:从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log.
步骤五:还会创建一个SQL线程,从relay log里面读取内容,从Exec_Master_Log_Pos位置开始执行读取到的更新事件,将更新内容写入到slave的db
超键、候选键、主键、外键分别是什么?
1、超键:在关系中能唯一标识元组的属性集称为关系模式的超键。一个属性可以为作为一个超键,多个属性组合在一起也可以作为一个超键。超键包含候选键和主键。
2、候选键:是最小超键,即没有冗余元素的超键。
3、主键:数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合。一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null)。
4、外键:在一个表中存在的另一个表的主键称此表的外键。