数据库
事务特性ACID是什么?
- 原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作的失败都会导致整个事务的失败;
- 一致性(Consistent):事务结束后系统状态是一致的;
- 隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态;
- 持久性(Durable):事务完成后所做的改动都会被持久化,即使发生灾难性的失败。通过日志和同步备份可以在故障发生后重建数据。
数据库隔离级别是有哪些?
数据库为我们提供的四种隔离级别:
- Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
- Repeatable read (可重复读):可避免脏读、不可重复读的发生。
- Read committed (读已提交):可避免脏读的发生。
- Read uncommitted (读未提交):最低级别,任何情况都无法保证。
什么是脏读、幻读、不可重复读、第一类丢失更新、第二类丢失更新?
- 脏读:指在一个事务处理过程里读取了另一个未提交的事务中的数据。
- 不可重复读:在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
- 幻读:幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
- 第一类丢失更新:事务A撤销时,覆盖事务B已经提交的数据,造成事务B所做操作丢失。
- 第二类丢失更新:事务A提交时,覆盖事务B已经提交的数据,造成事务B所做操作丢失。
MySQL的主从复制机制了解吗?
MySQL 的 Replication 是一个基于 Binary Log 文件复制的同步过程,从 Master instance 复制到 Slave instance。整个复制过程主要由三个线程来完成,其中两个线程(Sql线程和IO线程)在 Slave 端,另外一个线程(IO线程)在 Master 端。
- Slave 的 IO线程 连接上 Master,请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容
- Master 收到来自 Slave 的 IO线程 请求后,通过负责复制的 IO线程 根据请求信息读取指定日志指定位置之后的日志信息(含位置)返回给 Slave 端的 IO线程。
- Slave 的 IO线程 收到信息后,将接收到的日志内容依次写入到 Slave 端的 Relay Log 文件(mysql-relay-bin.xxxxxx)的最末端,并将读取到的 Master 端的 Binary Log 的文件名和位置记录到 master-info 文件中,以便在下一次读取的时候通知 Master 需要从哪个 Binary Log 文件的位置开始读取
- Slave 的 SQL线程 检测到 Relay Log 中新增加了内容后,会马上解析该 Log 文件中的内容,同步其中的行或Query,从而保证和 Master 数据一致
针对主从数据的一致性,MySQL 的 Replication 分为:
异步模式:MySQL默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理。注意:MySQL5.5版本以上才支持
完全同步模式:当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
半同步模式:介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。注意:半同步模式的主从同步超时仍会转为异步模式,待通讯正常时才会恢复为半同步模式
针对具体的数据复制,Binary Log内容包含:
基于SQL语句的复制:statement-based replication, SBR。
基于行的复制:row-based replication, RBR。
混合模式复制:mixed-based replication, MBR。(常用)
详情参考 MySQL半同步复制、mysql 主从复制原理、mysql数据库主从配置详解以及主从实现原理分析
MySQL集群你们是如何部署的?
目前公司的日订单量在10000左右,所以未做分库分表,仅做了读写分离处理,集群架构上采用的一主多从模式,同时为了保证各结点的高可用通过 Keepalived+Vip 的方式加了同步备份结点,备机正常情况下不提供服务。
主从+备机
- Master(192.168.31.230)为正常运行环境下的主库,为从库Slave(192.168.31.231)提供“主-从”复制功能(异步模式);
- Master_Backup(192.168.31.232)是Master的备份库,只要Master是正常的,它不对外提供服务。它与Master之间属于"主-主"复制关系,即自己既是主机,又是对方的从机;
- 同理,192.168.31.233为Slave_Backup,为192.168.31.231的备份库,只要Slave是正常的,对应的备份机不对外提供服务;
- Slave在此架构中的目的是为了实现读写分离,对应用程序来说,Master只负责写,Slave只负责读。Slave的数据来源于Master的复制操作;
- 如果Master由于某种原因(例如:宕机和断电等)导致不能正常运行,则此时需要让Master_Backup自动切换为新主机,而Slave和Slave_Backup也能自动切换数据源到Master_Backup;
- 同理,如果Slave由于某种原因(例如:宕机和断电等)导致不能正常运行,则此时需要让对应的Slave_Backup自动切换为新从机;
- 无论是Master还是切换后的Master_Backup,它们向客户端提供的连接地址应保持一致,如上图提供的VIP+Port,即192.168.31.201:3306,Slave和Slave_Backup也应如此,对外提供的连接地址始终是192.168.31.202:3306。
Mysql的MyISAM与InnoDB引擎了解吗?你们是如何使用的?
- InnoDB支持事务,MyISAM不支持,对于大部分业务表是需要支持事务的,所以InnoDB更为常用。
- MyISAM更适用于对查询性能要求较高的场景:比如历史订单查询(调度定期迁移订单表数据到历史订单表)。MyISAM索引和数据分离,而且其索引是压缩的,可以更好地利用内存。而InnoDB的索引和数据是紧密捆绑的,所以MyISAM的查询性能明显优于InnoDB。MyISAM还拥有全文索引的功能,这可以极大地优化LIKE查询的效率,而InnoDB是不支持的。虽然如此,但对于大数据量的存储,还是需要通过升级架构来解决,比如分表分库,而不是单纯地依赖存储引擎。
你们都使用过哪些类型的索引?这些索引的原理清楚吗?
常用的索引有:普通索引、唯一索引、主键索引、组合索引、全文索引(MyISAM的like查询场景),索引的存储类型主要有:B-Tree、Hash两种,它们的特性如下:
- Hash 索引结构(哈希表)的特殊性,检索效率非常高,一次定位,不像 B-Tree 索引需要从根节点到枝节点,效率远高于B-Tree。因为基于hash值检索,所以仅限于等值查询;另外其存储结构导致无法按任何方式排序;还有就是对于选择性较低的索引键,由于产生大量hash碰撞(碰撞时只能对相同的hash的值进行一一比较),查询性能会急剧下降。
- B-Tree 索引结构(平衡树)是通用的索引结构,相对于Hash索引结构,对于大部分的业务场景,查询效率更为稳定。
详情参考 MySQL的btree索引和hash索引的区别
Mysql索引的最左前缀匹配了解吗?
最左前缀匹配,对于组合索引,当查询条件精确匹配索引的左边连续一个或几个列时,
索引可以被用到,但是只能用到一部分,即条件所组成的最左前缀。
Mongodb用过吗?你们是如何使用的?
一般Mongodb用于存储查询频率较高的海量数据、数据结构灵活多变的业务场景,现在系统的订单轨迹、商品评价、商品属性信息都是用过Mongodb进行存储的。
- 使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
- 使用 MongoDB 存储商品属性信息,不同商品的属性差别非常大,数据结构不好设计,通过 MongoDB 基于Json存储的特点可以很方便的进行存储,降低程序设计复杂度的同时,也提升了商品信息的访问性能。
Mysql的索引优化了解吗?你们是如何优化的?
主要在索引的结构、SQL的查询两方面进行优化:
- 结构优化
- 在InnoDB中不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。
- 在InnoDB中不建议用非单调的字段作为主键,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。
- 在InnoDB中,如果没有特别的需要,请永远使用一个与业务无关的自增字段作为主键。
- 为了兼顾索引的大小和查询速度,可以通过前缀索引(截取字段左边部分内容构建索引)的方式优化索引。
- 尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
- 查询优化
- Where、Group by、Order by时使用的字段要尽可能遵循最左前缀原则,否则无法使用到索引。
- 对查询进行优化,应尽量避免全表扫描,首先应考虑在 Where、Group by、Order by 涉及的列上建立索引。
- 尽量使用覆盖索引(只访问索引的查询(查询的列与索引的列一致),避免由主键索引再次回查),尽量避免select * 。
- 优化关联查询
- 关联查询的表仅需在外键列所在的子表建立索引即可,关联主表无需创建,否则反而会增加负担。
- 确保Group by和Order by中的表达式只涉及到一个表的列,这样才有可能使用索引来优化这个过程。
- 尽量避免查询无法使用索引的情况:
- Mysql5.7之前查询条件中含有函数或表达式时无法使用索引,故查询条件中尽量避免出现表达式,而是先手动运算,转换为无表达式的查询语句。
- 字段值为Null时无法使用索引,故要索引的字段在创建时要尽可能设置默认值。
- 在 where 子句中使用!=或<>操作符时字段无法使用索引。
- like以通配符(%)开头的字段查询无法使用索引,故查询应尽量避免此种情况。
- 在 where 子句中使用or时,必须or表达式中的字段都建有索引,否则无法使用索引,若要使用索引可以通过union替代or。
Mysql加锁过程了解吗?
详情参见:Mysql加锁过程详解
数据库锁都有哪几类,它们的原理是什么?锁与事务的关系了解吗?
详情参考:理解innodb的锁(record,gap,Next-Key lock)
Mysql更新时哪些情况使用行锁,哪些情况使用表锁?如何对锁的使用情况进行具体的分析?
更新时除了。详情参见:不恰当的update语句使用主键和索引导致mysql死锁、MySQL并发更新数据加锁处理
数据量很大且访问量很高的数据库表在更新时应尽可能避免仅根据非主键的几个字段就进行更新。而是应该首先将要更新的记录查出来然后逐条按主键id更新。