您现在的位置是:首页 > php PHP面试八股文 马力 2023-07-31 21:52:03 【php】 2060人已围观 简介PHP面试八股文 [更多详情请查看小马的知识库](https://docx.maliweb.top/#/) #1. mysql ## 1.1 有哪些事务隔离级别,Mysql 的事务隔离级别是怎么实现的?(每家都问) 从上往下,隔离强度逐渐增强,性能逐渐变差。采用哪种隔离级别要根据系统需求权衡决定,其中,可重复读是 MySQL 的默认级别。 1. 读未提交(Read Uncommitted): 最低级别的隔离,事务可以读取其他事务未提交的数据。 这可能导致脏读(Dirty Read),即读取到其他事务尚未提交的数据。 2. 读提交(Read Committed): 大多数数据库的默认隔离级别,事务只能读取已经提交的数据。 这可以避免脏读,但可能会导致不可重复读(Non-repeatable Read),即在同一个事务中,两次读取同一行数据的结果可能不同。 3. 可重复读(Repeatable Read): 事务执行期间始终保持一致的快照视图,确保在同一个事务中多次读取同一数据时,结果是一致的。 但在这个级别下,可能会出现幻读(Phantom Read),即同一个查询在不同时间点返回不同的结果集。 4. 串行化(Serializable): 最高级别的隔离,事务顺序执行,避免了脏读、不可重复读和幻读,但也导致了最大的并发性能损失,因为所有事务都必须顺序执行。 要实现MVCC,MySQL在每行数据中通常会包含以下两个重要的隐藏字段: 1. 创建版本号(Transaction ID):用于标识该数据行的版本,每个事务对数据进行修改时,会分配一个新的版本号。 2. 删除版本号(Delete Transaction ID):用于标识删除操作的版本号,当一个事务删除了某行数据时,会记录一个删除版本号,表示该数据已被删除。 MVCC的核心思想是为每个数据行(或数据项)维护多个版本,每个版本对应一个特定的事务状态。这样,在每个事务开始时,它会获得一个时间点的快照视图,只能看到在该时间点之前已经提交的数据版本。这样的设计确保了读取的数据在整个事务过程中保持一致性,不会受到其他事务的干扰,从而实现了隔离性。 下面详细讲解MVCC的工作原理: 1. 版本号和快照视图: 每个数据行通常会有一个唯一的版本号,用于标识该数据行的版本。在MySQL中,通常使用事务ID(Transaction ID)作为版本号。 每个事务在开始时都会被分配一个唯一的事务ID。在事务执行期间,事务ID不会改变。 事务的快照视图是一个时间戳,代表事务开始时的时间点。它记录了事务开始时所有已经提交的事务ID。 通过比较数据行的版本号和事务的快照视图,MySQL可以确定哪些数据版本对于该事务是可见的。 2. 读操作: 当一个事务开始执行读操作时,它会获取一个快照视图,记录了所有已经提交的事务ID。 在读取数据行时,MySQL会比较数据行的版本号和事务的快照视图: 如果数据行的版本号小于等于快照视图中的最大事务ID,表示该数据版本是可见的,事务可以读取该数据。 如果数据行的版本号大于快照视图中的最大事务ID,表示该数据版本是不可见的,事务不能读取该数据,需要等待该数据行的事务提交或者回滚。 3. 写操作: 当一个事务执行写操作(插入、更新、删除)时,它会获得一个全局写锁,用于保证写操作的原子性和独占性。 在写操作执行过程中,MySQL会为该数据行创建一个新的版本,将当前事务ID作为新版本的版本号,并更新数据行的内容。 其他事务在读取该数据行时,会根据快照视图来决定可见的数据版本,新写入的版本对于那些开始时间早于当前事务的读操作是不可见的,从而保持了隔离性。 4. 清理过期版本: 随着事务的不断进行,旧的数据版本可能会变得无用,因为它们对新的事务已经不可见。 MySQL通过一定的机制定期清理过期的数据版本,释放存储空间,并确保数据库的性能。 通过这些版本号的控制和比较,MySQL能够根据事务的隔离级别,决定在每个事务中可见的数据行版本,从而实现不同的隔离级别。 ##1.2 索引原理(每家都问) [参考文档](https://relph1119.github.io/mysql-learning-notes/#/mysql/06-%E5%BF%AB%E9%80%9F%E6%9F%A5%E8%AF%A2%E7%9A%84%E7%A7%98%E7%B1%8D-B+%E6%A0%91%E7%B4%A2%E5%BC%95) ##1.3 分库分表的策略,如果要按照分表字段以外的字段作为查询条件怎么办(每家都问) 分库分表是一种常见的数据库水平拆分策略,用于应对大规模数据的存储和查询需求。在分库分表的情况下,如果要按照分表字段以外的字段作为查询条件,可以使用两种主要方法:联合查询和分布式索引。 1. 联合查询: 联合查询是指在多个分表中进行查询,并将结果进行合并。当查询涉及到多个分表,而其中有些条件是在分表字段以外的字段上时,可以先分别查询各个分表,然后将结果合并,得到最终结果。 例如,如果按照用户ID进行分表,而要查询特定日期范围内的用户登录记录,可以先确定涉及到的分表,然后分别在每个分表上按照日期范围进行查询,最后将结果合并。 联合查询的优点是可以利用每个分表的索引来提高查询性能,但缺点是需要在应用层进行结果的合并,可能会增加应用开发的复杂性。 2. 分布式索引: 分布式索引是在分库分表的情况下,额外创建一个全局索引来覆盖需要查询的字段。这个全局索引可以存在于另外一个单独的数据库或缓存中,用于快速定位符合查询条件的数据在哪些分表上。 例如,如果按照用户ID进行分表,但需要根据用户名来查询用户信息,可以在另外一个数据库中创建一个以用户名为索引字段的表,记录用户名对应的用户ID和所在的分表信息。在查询时,首先在分布式索引表中查找用户名对应的用户ID和分表信息,然后再去相应的分表中查询数据。 分布式索引的优点是可以快速定位符合查询条件的数据所在的分表,减少了查询时需要扫描的分表数量,从而提高了查询性能。但缺点是需要额外的维护工作来保持分布式索引的一致性和更新。 在选择分库分表的策略时,需要综合考虑查询的性能需求、数据分布情况和应用开发的复杂性。不同的业务场景可能适合不同的解决方案。 ##1.4 MVCC 和间隙锁原理(滴滴 字节 百度) MVCC(Multi-Version Concurrency Control)和间隙锁是MySQL数据库中用于实现并发控制的两种重要机制。它们分别处理并发事务读写操作和防止幻读问题。 1. MVCC(Multi-Version Concurrency Control)原理: MVCC是一种并发控制机制,用于保证数据库在多个事务同时进行读写操作时的隔离性。MVCC的核心思想是为每个数据行维护多个版本,每个版本对应一个特定的事务状态。每个事务在开始时会获得一个时间点的快照视图,只能看到在该时间点之前已经提交的数据版本。这样,当其他事务对数据进行修改时,对于正在运行的事务来说,看到的仍然是之前的版本,保持了事务的隔离性。 MVCC的关键实现包括: 创建版本号(Transaction ID):每个数据行通常会有一个唯一的版本号,用于标识该数据行的版本,每个事务对数据进行修改时,会分配一个新的版本号。 删除版本号(Delete Transaction ID):用于标识删除操作的版本号,当一个事务删除了某行数据时,会记录一个删除版本号,表示该数据已被删除。 快照视图(Snapshot View):事务开始时会获得一个时间点的快照视图,记录了在该时间点之前已经提交的事务ID。 2. 间隙锁原理: 间隙锁是MySQL中用于防止幻读问题的一种锁机制。幻读是指在一个事务中执行了某个范围的查询操作,然后另一个事务插入了一条新的数据,再次执行相同的查询时会发现新增的数据,产生了幻觉。间隙锁可以防止其他事务在锁定范围内插入新的数据,从而避免幻读问题。 当一个事务执行范围查询时(例如,SELECT * FROM table WHERE col BETWEEN 10 AND 20),MySQL会对查询范围的间隙(不存在的数据行)应用间隙锁。 间隙锁锁定了范围内的间隙,防止其他事务在这个范围内插入新的数据。 间隙锁只有在事务执行范围查询时才会被应用,在普通的单行查询中不会使用间隙锁。 总结:MVCC和间隙锁是MySQL中重要的并发控制机制。MVCC通过为每个数据行维护多个版本,实现了并发事务的隔离性。间隙锁用于防止幻读问题,通过锁定范围内的间隙,阻止其他事务在该范围内插入新的数据。这两个机制共同确保了数据库在高并发环境下的数据一致性和隔离性。 ##1.5 explain 的 type 字段有哪些(知乎) system:系统表,少量数据,往往不需要进行磁盘IO const:常量连接 eq_ref:主键索引(primary key)或者非空唯一索引(unique not null)等值扫描 ref:非主键非唯一索引等值扫描 range:范围扫描 index:索引树扫描 ALL:全表扫描(full table scan) type扫描方式由快到慢 system > const > eq_ref > ref > range > index > ALL ##1.6 update 语句的执行流程,binlog 的作用和几种格式(滴滴) ###UPDATE 语句的执行流程: 1. 解析:MySQL 首先会解析 UPDATE 语句,确定需要更新的表、更新的字段、更新的条件等信息。 2. 查询优化器:MySQL 使用查询优化器分析查询,并选择最优的执行计划。优化器会考虑哪些索引可用,以及如何使用它们来尽可能高效地更新数据。 3. 锁定:在执行更新之前,MySQL 会根据更新语句中的条件对相关的行进行加锁,以确保在更新过程中没有其他并发事务对数据进行修改。 4. 执行更新:MySQL 根据优化后的执行计划执行更新操作,将满足更新条件的行进行相应的修改。 5. 更新 binlog:在执行更新操作后,MySQL 会将该更新操作记录到 binlog 中。binlog 是 MySQL 的二进制日志,它记录了数据库中所有的写操作,如插入、更新、删除等。 ###binlog 的作用有以下几个方面: 1. 数据复制:binlog 是 MySQL 主从复制的基础。当设置了主从复制后,主数据库会将写操作记录到 binlog 中,从数据库通过读取主数据库的 binlog 来实现数据复制,保持主从数据的一致性。 2. 数据恢复:binlog 可以用于数据恢复。通过回放 binlog 中的写操作,可以还原数据库到特定时间点的状态。 3. 数据备份:binlog 是 MySQL 数据库备份的一种方式。通过备份 binlog,可以在数据丢失时进行数据恢复。 ###MySQL 的 binlog 有三种格式: 1. Statement 格式:这是最早的 binlog 格式。它记录的是执行 SQL 语句本身,比如 UPDATE, INSERT, DELETE 等。这种格式的优点是简单且易于理解,但是在某些情况下可能会出现非确定性的结果,因为不同的执行顺序可能会导致不同的结果。 2. Row 格式:这种格式将更新的每一行数据都记录下来。它记录了哪些行被修改以及修改后的值,不依赖于具体的 SQL 语句,因此可以保证更新的结果是确定性的。但是由于记录了每一行的变化,可能会导致 binlog 较大。 3. Mixed 格式:这是一种折中的方式。MySQL 根据具体的 SQL 语句选择使用 Statement 格式或 Row 格式进行记录。一般情况下,对于简单的语句使用 Statement 格式,对于复杂的语句使用 Row 格式。 可以通过 MySQL 的配置参数 binlog_format 来设置 binlog 的格式。不同的格式有不同的适用场景,需要根据具体的需求进行选择。 ##1.7 主从同步的原理和问题(字节 滴滴 陌陌) 主从同步(Master-Slave Replication)是 MySQL 数据库的一种常见复制技术,它用于将一个数据库服务器(主服务器)的数据复制到其他多个数据库服务器(从服务器),从而实现数据的冗余备份、读写分离和负载均衡等目的。以下是主从同步的原理和一些常见问题: ###主从同步的原理: 1. 主服务器(Master):主服务器负责处理客户端的读写请求和更新操作,所有的写操作都在主服务器上执行。 2. 从服务器(Slave):从服务器通过连接到主服务器并复制主服务器上的二进制日志(binlog),从而获取主服务器上的写操作。 3. 二进制日志(binlog):主服务器记录所有的写操作(INSERT、UPDATE、DELETE)到二进制日志中,从服务器读取并执行这些日志,从而保持数据的同步。 4. 主从同步进程:MySQL 服务器上运行一个主从同步进程,负责在从服务器上应用主服务器的二进制日志,使从服务器的数据与主服务器保持一致。 ###主从同步常见问题: 1. 延迟:由于网络延迟、硬件性能等原因,从服务器上的数据可能会有一定程度的延迟。这意味着在主服务器上进行写操作后,从服务器上的数据不会立即更新。 2. 主从切换:当主服务器发生故障或需要进行维护时,需要将一个从服务器切换为新的主服务器。这个过程可能需要手动干预,并且可能导致一些数据丢失。 3. 数据一致性:主从同步是异步的过程,由于网络问题或其他故障,可能导致主从数据不一致的情况。因此,在应用程序中,需要考虑主从数据同步的一致性问题。 4. 主从延迟导致的读写分离问题:由于从服务器上的数据可能有一定的延迟,当应用程序进行读写分离时,可能导致读取到的数据不是最新的,从而引发问题。 5. 自增主键冲突:如果主从服务器上都存在自增主键的表,并且同时进行插入操作,可能导致自增主键冲突的问题。 6. 网络故障:网络故障可能导致主从同步中断,需要及时恢复连接并处理数据同步。 注意: 主从同步虽然是常用的复制技术,但并不是完全没有风险的。在设置主从同步时,需要仔细考虑和规划,确保数据的一致性和可用性。同时,也需要定期监控主从同步状态,及时发现并解决潜在的问题。 ##1.8 发生死锁的原因以及如何解决(滴滴 顺丰) ###死锁产生的原因: 死锁是由于资源竞争和资源请求的方式不当所导致的。当满足以下四个条件时,系统可能陷入死锁状态: 1. 互斥条件:每个资源同时只能由一个进程(线程)占用,其他进程(线程)必须等待。 2. 请求与保持条件:一个进程(线程)在持有某些资源的同时,又请求获取其他资源。 3. 不可剥夺条件:已经分配给一个进程(线程)的资源不能被其他进程(线程)剥夺,只能由进程(线程)自己释放。 4. 循环等待条件:多个进程(线程)之间形成一个环路,每个进程(线程)都在等待下一个进程(线程)所占有的资源。 ###死锁的解决方法: 1. 预防死锁: 通过合理的系统设计避免死锁发生。例如,对资源进行分类,并规定资源的申请顺序,使得进程只能按照一定顺序请求资源,从而避免循环等待条件。 确保进程在申请资源时一次性申请所需的全部资源,而不是一次请求一个,从而避免请求与保持条件。 2. 避免死锁: 使用资源分配策略来避免死锁。著名的避免死锁算法之一是银行家算法(Banker's algorithm),它根据系统当前状态来判断是否可以安全地分配资源,以避免进入死锁状态。 3. 检测与解除死锁: 允许死锁发生,但通过周期性地检测系统状态来解除死锁。常用的死锁检测算法是资源分配图(Resource Allocation Graph)算法,它可以检测是否存在死锁以及确定造成死锁的进程(线程)。 如果检测到死锁,系统可以采取一些措施来解除死锁。例如,选择一个或多个进程进行终止,回收其持有的资源,或者通过进程回退(Rollback)来解除死锁。 4. 鸵鸟策略: 忽视死锁问题,假设死锁很少发生,或者死锁发生后由系统管理员手动干预处理。这种方法通常不是一个理想的解决方案,因为死锁可能会导致系统崩溃或长时间无响应。 解决死锁问题并不是一件简单的任务,因为不同的解决方法可能会涉及到对系统性能和资源利用率的权衡。在设计和实现系统时,需要综合考虑系统的需求、资源分配策略、以及可能出现的死锁情况,来选择适合的死锁解决方法。 ##1.9 如何优化大 offset(陌陌) 优化大 OFFSET 是指在数据库查询中,当需要跳过大量行数时(例如:在分页查询中),使用大 OFFSET 值会导致查询效率下降。这是因为数据库需要按顺序扫描并跳过指定数量的行,这种操作在大数据集上会变得非常昂贵。 下面提供一些优化大 OFFSET 的方法: 1. 使用合适的索引:确保查询涉及的字段上有合适的索引,尤其是用于排序和筛选的字段。索引能够加速查询,并减少大 OFFSET 时的数据扫描量。 2. 使用主键或唯一索引定位:如果你的数据有自增主键或唯一索引,你可以通过设置 WHERE 条件来定位指定页的起始位置,然后使用较小的 LIMIT 值来获取指定数量的行。 3. 使用游标分页:对于支持游标的数据库引擎(例如 MySQL 的 InnoDB 引擎),可以使用游标分页技术,通过保存上一次查询的位置,在下一次查询时继续查询数据,避免使用大 OFFSET。 4. 使用无限滚动(Infinite Scrolling):对于前端应用,可以使用无限滚动替代传统的分页查询。这样在滚动时逐渐加载更多数据,避免使用大 OFFSET。 5. 数据预处理和缓存:对于数据量非常大的情况,可以在后台进行预处理和缓存,将查询结果缓存起来,这样在前端查询时不需要每次都执行大 OFFSET 查询。 6. 避免使用大 OFFSET:如果可能的话,尽量避免使用大 OFFSET。可以在数据库设计和应用程序中优化数据展示的方式,以避免对大数据集进行跳跃式的查询。 总的来说,优化大 OFFSET 主要是通过合理设计数据库索引、选择合适的查询方式以及前端优化来减少查询成本。需要根据具体的应用场景和需求来决定采用哪种优化策略。 #2. redis ##2.1 缓存如何保证一致性(每家都问) ###2.1.1先删缓存,再更新数据库 先删除缓存,数据库还没有更新成功,此时如果读取缓存,缓存不存在,去数据库中读取到的是旧值,缓存不一致发生。 ####解决方案 延时双删 延时双删的方案的思路是,为了避免更新数据库的时候,其他线程从缓存中读取不到数据,就在更新完数据库之后,再sleep一段时间,然后再次删除缓存。 sleep的时间要对业务读写缓存的时间做出评估,sleep时间大于读写缓存的时间即可。 流程如下: 线程1删除缓存,然后去更新数据库 线程2来读缓存,发现缓存已经被删除,所以直接从数据库中读取,这时候由于线程1还没有更新完成,所以读到的是旧值,然后把旧值写入缓存 线程1,根据估算的时间,sleep,由于sleep的时间大于线程2读数据+写缓存的时间,所以缓存被再次删除 如果还有其他线程来读取缓存的话,就会再次从数据库中读取到最新值 ###2.1.2先更新数据库,再删除缓存 如果反过来操作,先更新数据库,再删除缓存呢? 这个就更明显的问题了,更新数据库成功,如果删除缓存失败或者还没有来得及删除,那么,其他线程从缓存中读取到的就是旧值,还是会发生不一致。 ####解决方案 消息队列 这是网上很多文章里都有写过的方案。但是这个方案的缺陷会更明显一点。 先更新数据库,成功后往消息队列发消息,消费到消息后再删除缓存,借助消息队列的重试机制来实现,达到最终一致性的效果。 这个解决方案其实问题更多。 引入消息中间件之后,问题更复杂了,怎么保证消息不丢失更麻烦 就算更新数据库和删除缓存都没有发生问题,消息的延迟也会带来短暂的不一致性,不过这个延迟相对来说还是可以接受的 进阶版消息队列 为了解决缓存一致性的问题单独引入一个消息队列,太复杂了。 其实,一般大公司本身都会有监听binlog消息的消息队列存在,主要是为了做一些核对的工作。 这样,我们可以借助监听binlog的消息队列来做删除缓存的操作。这样做的好处是,不用你自己引入,侵入到你的业务代码中,中间件帮你做了解耦,同时,中间件的这个东西本身就保证了高可用。 当然,这样消息延迟的问题依然存在,但是相比单纯引入消息队列的做法更好一点。 而且,如果并发不是特别高的话,这种做法的实时性和一致性都还算可以接受的。 ####其他解决方案 设置缓存过期时间 每次放入缓存的时候,设置一个过期时间,比如5分钟,以后的操作只修改数据库,不操作缓存,等待缓存超时后从数据库重新读取。 如果对于一致性要求不是很高的情况,可以采用这种方案。 这个方案还会有另外一个问题,就是如果数据更新的特别频繁,不一致性的问题就很大了。 在实际生产中,我们有一些活动的缓存数据是使用这种方式处理的。 因为活动并不频繁发生改变,而且对于活动来说,短暂的不一致性并不会有什么大的问题。 为什么是删除,而不是更新缓存? 我们以先更新数据库,再删除缓存来举例。 如果是更新的话,那就是先更新数据库,再更新缓存。 举个例子:如果数据库1小时内更新了1000次,那么缓存也要更新1000次,但是这个缓存可能在1小时内只被读取了1次,那么这1000次的更新有必要吗? 反过来,如果是删除的话,就算数据库更新了1000次,那么也只是做了1次缓存删除,只有当缓存真正被读取的时候才去数据库加载。 ####总结 首先,我们要明确一点,缓存不是更新,而应该是删除。 删除缓存有两种方式: 先删除缓存,再更新数据库。解决方案是使用延迟双删。 先更新数据库,再删除缓存。解决方案是消息队列或者其他binlog同步,引入消息队列会带来更多的问题,并不推荐直接使用。 针对缓存一致性要求不是很高的场景,那么只通过设置超时时间就可以了。 其实,如果不是很高的并发,无论你选择先删缓存还是后删缓存的方式,都几乎很少能产生这种问题,但是在高并发下,你应该知道怎么解决问题。 ##2.2. 用过 redis 哪些数据结构,使用场景是什么(每家都问) [参考文档](https://juejin.cn/post/6844903951502934030) Redis(Remote Dictionary Server)是一种内存数据存储系统,它支持多种数据类型,每种数据类型都有其特定的使用场景。以下是一些常见的 Redis 数据类型及其使用场景: 1. 字符串(String): 使用场景:存储任意类型的数据,如用户会话信息、计数器、缓存等。 2. 哈希(Hash): 使用场景:存储对象的字段和值,适用于存储用户属性、配置项等。 3. 列表(List): 使用场景:按照插入顺序存储多个元素,适用于消息队列、最新消息列表等。 4. 集合(Set): 使用场景:存储不重复的元素,适用于标签系统、好友关系等。 5. 有序集合(Sorted Set): 使用场景:类似于集合,但每个元素有一个关联的分数,适用于排行榜、按照分数排序的数据等。 6. 位图(Bitmap): 使用场景:用于跟踪状态,如用户签到、在线状态等。 7. 地理空间索引(Geospatial Index): 使用场景:存储地理位置信息,适用于地理位置搜索、附近的位置等。 8. HyperLogLog: 使用场景:估计集合的基数(不重复元素的数量),适用于统计分析、基数估计等。 9. 流(Stream): 使用场景:按照时间顺序存储事件数据,适用于日志、事件追踪等。 这些数据类型可以灵活地组合使用,以满足各种不同的应用需求。Redis 还提供了丰富的命令和功能,使开发者能够高效地操作和管理数据。需要根据具体的应用场景来选择适合的数据类型。 ##2.3 redis 的 connect 和 pconnect 的区别,pconnect 有什么问题(滴滴 陌陌) ###区别 - ####1. connect: connect 是普通的连接函数。每次调用 connect 都会创建一个新的连接到 Redis 服务器。 每当执行 connect,都会建立一个新的 TCP 连接,并且每个连接都是独立的,不会共享连接。 适用于短期的操作,每次操作完成后都会主动断开连接。 - ####2. pconnect (Persistent Connect): pconnect 是持久连接函数。它会尝试复用现有的连接,如果没有可用的连接,则会创建一个新的连接。 在使用 pconnect 时,连接会被保持在脚本执行的整个生命周期中,可以被多个页面和多次脚本调用复用。 这样可以减少每个请求创建和关闭连接的开销,提高性能,特别是在高并发场景下。 ###问题和注意事项: 持久连接不会在每次请求结束后自动关闭,而是会保持打开状态。这可能导致连接数的累积,如果并发量非常高,可能会导致服务器上连接数耗尽。 如果在连接过程中出现问题,例如服务器断开连接或者网络中断,pconnect 不会报错,而是会继续使用这个已经失效的连接,这可能导致操作失败或者数据丢失。 在使用 pconnect 时,需要格外小心处理 Redis 的连接超时、网络中断、服务器重启等异常情况,以确保连接的可靠性和正确性。 当连接池中的所有连接都处于被占用状态时,新的请求将会被阻塞,直到有一个连接可用或者超时。 综上所述,使用 pconnect 需要谨慎对待,尤其在高并发或者异常情况下。如果不是特别需要长时间持有连接,建议在使用 Redis 时使用普通的 connect,每次操作完成后主动关闭连接,确保连接的及时释放。如果需要优化性能,可以考虑使用连接池技术,以确保高并发时的连接可用性和稳定性。 ##2.4 redis 如何实现分布式锁,有什么问题(陌陌) [参考文章](https://juejin.cn/post/6936956908007850014) ##2.5 redis 为什么用跳表实现有序集合?原理,用有序集合的场景(字节 滴滴) Redis使用跳表来实现有序集合的主要原因是跳表具有较高的查询效率和空间效率。 ###跳表原理: 跳表(Skip List)是一种基于并联链表的数据结构,可以在O(log n)的时间内进行元素的查找、插入和删除操作。相对于平衡二叉树等其他数据结,跳表具有简单易解、易实现、效率稳定、适应高并发等优点。 跳表的原理是通过在普通链表的基础上增加多级索引,索引层次越高,节点跨度越大,从而可以加速查找操作。当需要查找某个元素时,跳表会按照索引层次逐层进行查找,直到找到目标元素或者确定目标元素不存在。 在Redis中,有序集合是指存储了一个有序元素列表的数据结构,每个元素都与一个浮点数值(分数)相关联,通过分数来对元素进行排序。 ###使用跳表来实现有序集合具有以下优势: 1. 时间复杂度:跳表能够实现O(log n)的查询复杂度,非常适合处理大规模有序数据。 2. 空间效率:相比于平衡二叉树等数据结构,跳表占用的空间较小,仅需O(n)的额外空间来存储索引层次。 ###有序集合在Redis中广泛应用于以下场景: 1. 排行榜:可以有集合存储用户的分数信息,根据分数进行排名和查询操作。 2. 范围查找:由于有序集合的元素是有序的,可以便捷地进行范围查找,例如查找某个时间段内的数据。 3. 去重:有序集合中的元素都是唯一的,可以用于去重操作。 4. 优先级队列:有序集合可以作为一种优先级队列,按照分数进行排序并快速获取最高/最低分数的元素。 5. 时间轴:可以将时间作为有序集合的分数,将事件按照时间顺序存储并进行查询。 总之,Redis使用跳表实现有序集合既能够保证高效的性能,又能节省空间开销,适用于多种有序数据处理场景。 ##2.6 主从同步的原理,哨兵和集群的区别(滴滴) [参考文章](https://www.cnblogs.com/chenwenyin/p/13549492.html) ##2.7 redis cluster 用的什么协议同步数据,哨兵的选举呢(陌陌) ###同步数据协议: - Redis Cluster使用的是Redis自定义的二进制协议来同步数据。在Redis Cluster中,节点之间使用Gossip协议来进行信息传递和状态同步。Gossip协议是一种去中心化的协议,节点之间相互通过广播信息,从而达到信息传递和状态同步的目的。 ###哨兵的选举原理: - 关于哨兵(Sentinel)的选举,哨兵是用于监控和管理Redis主从复制的进程。当主节点出现故障时,哨兵会自动选举新的主节点。哨兵之间也使用Redis自定义的二进制协议来进行信息交换和状态同步。选举的过程是通过一种类似Raft的协议来实现的,哨兵之间相互进行通信,协商选举出新的主节点。在选举过程中,哨兵会比较各个候选主节点的复制偏移量和优先级等信息来做出最终的决策。 - 需要注意的是,哨兵是独立于Redis Cluster的,Redis Cluster是用于分布式存储和高可用的Redis解决方案,而哨兵是用于监控和管理单个Redis实例的高可用性。哨兵可以配合Redis Cluster使用,也可以单独用于普通的Redis主从复制架构。 ##2.8 rdb 和 aof 的原理(滴滴 高德) [参考文档](https://juejin.cn/post/7006619052453937160) ###RDB(Redis Database): - RDB 是 Redis 的快照持久化方式,它在指定的时间间隔内将数据集保存到磁盘。当启用 RDB 时,Redis 会将当前内存中的数据保存到一个二进制文件中。这个过程通常是异步进行的,因此 Redis 仍然可以继续处理客户端请求。 #### RDB 的生成过程是这样的: Redis 定期(通过配置选项设置的时间间隔)将内存中的数据快照写入一个临时文件。 一旦快照生成完毕,Redis 将快照文件重命名为一个配置指定的持久化文件(通常是 .rdb 后缀)并替换旧的持久化文件。 ####优点: RDB 文件非常紧凑,适合备份和迁移。 在恢复大数据集时比 AOF 启动更快。 #### 缺点: 可能会丢失最后一次快照之后的数据,因为 RDB 是周期性的。 对于非常频繁的数据更新,可能会导致较长时间的数据丢失。 ###AOF(Append-Only File): - AOF 是另一种持久化方式,它记录了每个写操作的日志,将 Redis 执行的每个写命令都追加到一个文件中。这种方式比 RDB 更加实时,因为它记录了每个命令的执行情况。 #### AOF 的生成过程是这样的: Redis 接收到一个写操作的命令(例如 SET key value),它会将该命令追加到 AOF 文件中。 当 AOF 文件变得较大时,Redis 可以根据配置选项对 AOF 文件进行重写,以减小文件的大小。 #### 优点: 更实时,因为每个写操作都会被记录,数据丢失更少。 可以在配置中设置不同的 fsync 选项,以平衡持久化和性能之间的关系。 #### 缺点: AOF 文件通常比 RDB 文件大,可能占用更多磁盘空间。 启动时恢复速度可能比 RDB 模式慢。 ##2.9 数据过期和淘汰策略(滴滴 高德 字节) [参考文章](https://xiaolincoding.com/redis/module/strategy.html#%E5%A6%82%E4%BD%95%E8%AE%BE%E7%BD%AE%E8%BF%87%E6%9C%9F%E6%97%B6%E9%97%B4) ##2.10 缓存雪崩 击穿 穿透(滴滴 陌陌) [参考文章](https://xiaolincoding.com/redis/cluster/cache_problem.html#%E4%BB%80%E4%B9%88%E6%98%AF%E7%BC%93%E5%AD%98%E9%9B%AA%E5%B4%A9%E3%80%81%E5%87%BB%E7%A9%BF%E3%80%81%E7%A9%BF%E9%80%8F) #3. php ##3.1 php-fpm 的生命周期,创建进程方式,各自的优缺点(腾讯 百度 滴滴 陌陌) ####生命周期: 1. 启动阶段:PHP-FPM 在启动时读取配置文件,根据配置项初始化进程池和相关参数。预先创建指定数量的 PHP 子进程,这些进程等待来自 Web 服务器(如 Nginx 或 Apache)的连接和请求。 2. 运行阶段:一旦 PHP-FPM 进程池启动,进程会一直保持活动状态,等待请求的到来。当请求到达时,Web 服务器将请求传递给空闲的 PHP-FPM 进程,进程处理请求并返回响应。这种模式使得进程可以复用,减少了创建和销毁进程的开销。 3. 终止阶段:PHP-FPM 可能在达到一定条件时终止或重启进程。这些条件可以包括请求超时、达到最大请求数、空闲时间过长等。PHP-FPM 进程管理器会根据这些条件自动终止不再需要的进程,以释放资源。 ####创建进程方式: 1. 静态创建(Static Mode): 优点:稳定,预先创建一定数量的进程,无论请求量如何,进程数保持不变,避免了进程创建和销毁的开销。 缺点:资源浪费,可能导致进程数过多,浪费内存和处理能力。 2. 动态创建(Dynamic Mode): 优点:根据实际请求量动态创建和终止进程,更高效地利用资源,适应负载波动。 缺点:进程创建和终止带来额外开销,可能在负载波动时导致进程频繁创建和终止,产生“进程抖动”。 3. 按需创建(OnDemand Mode): 优点:最节省资源,仅在有请求时才创建进程,适应不规律的负载模式。 缺点:首次请求响应时间较长,因为需要等待进程创建,可能在高并发时导致性能下降。 ####各自的优缺点: 1. 静态创建: 优点:稳定,适用于稳定负载,能够确保有足够的进程处理请求。 缺点:资源浪费,不适应负载波动,可能导致性能不佳。 2. 动态创建: 优点:适应负载波动,根据请求量动态分配进程,有效利用资源,提供更好的性能。 缺点:进程创建和终止开销,可能导致在负载波动时产生进程抖动,影响性能。 3. 按需创建: 优点:最节省资源,仅在需要时创建进程,适应不规律的负载,可以减少资源浪费。 缺点:首次请求响应时间较长,可能在高并发下性能下降,不适用于需要即时响应的场景。 选择适当的创建方式应根据应用的性能需求、负载特征以及资源限制来决定。动态创建通常是一种平衡各方面考虑的常见选择,但在特定情况下,其他方式也可能更为合适。 ##3.2 php 数组遍历为什么能保证有序(滴滴) [参考文档](https://segmentfault.com/a/1190000019964143) ##3.3 php 怎么实现的弱类型,怎么实现一个扩展(腾讯) ##3.4 常见魔术方法和函数(腾讯 滴滴) [魔术方法参考文档](https://learnku.com/php/t/40919) #4. es ##4.1 深度分页会有什么问题(滴滴 百度 陌陌) ###Elasticsearch(ES)是一个分布式的搜索和分析引擎,用于存储和检索大量数据。在进行深度分页(大量跳过数据进行分页)时,可能会遇到一些问题和性能方面的挑战,如下所示: 性能问题:深度分页涉及跳过大量文档,这可能导致性能下降。随着跳过的文档数量增加,查询的执行时间可能会线性增加,影响查询性能。 内存压力:跳过大量文档可能会导致 Elasticsearch 分片(shard)需要加载更多的数据到内存中,增加内存的使用量。这可能会导致分片的内存压力增加,影响整体性能。 深度分页限制:Elasticsearch 有一个默认的深度分页限制(通常是 10000),即一次查询最多返回指定数量的文档。超过这个限制,Elasticsearch 会返回一个警告,因为大量跳过文档可能会影响性能。 稳定性问题:深度分页可能导致查询的执行时间增加,从而增加了查询执行期间网络中断、分片故障或超时等问题发生的可能性。 搜索结果不稳定:深度分页可能导致文档被删除或更新,从而在多次查询中返回不一致的搜索结果。这是由于分布式系统中的异步索引更新和删除操作可能导致搜索结果的不稳定性。 ####为了解决这些问题,建议采取以下措施: 使用游标(Scroll):Elasticsearch 提供了游标机制,可以通过游标来获取大量数据,避免一次性加载所有数据到内存中。这有助于减轻内存和性能压力。 合理设置分页大小:根据查询的性质和应用需求,合理设置分页大小,尽量避免使用过大的分页大小。 使用深度分页警告:当分页数量接近限制时,确保监听 Elasticsearch 返回的警告信息,以便及时调整查询策略。 优化查询和索引:通过调整查询、使用索引和过滤条件等方式,优化查询以提高性能。 定期重建索引:定期重建索引可以帮助减少分片中的删除标记和过时数据,提高搜索性能和结果的稳定性。 深度分页在某些情况下是必要的,但需要仔细权衡性能和结果的稳定性,以确保系统的稳定运行。 ##4.2 倒排索引的原理(字节 高德) [参考文档](https://xiaoming.net.cn/2020/11/25/Elasticsearch%20%E5%80%92%E6%8E%92%E7%B4%A2%E5%BC%95/) ##4.3 lsm 树原理(字节) [参考文档](https://zhuanlan.zhihu.com/p/181498475) #5. kafka ##5.1 kafka 的架构,大致储存结构(高德 字节 滴滴) [参考文档](https://zhuanlan.zhihu.com/p/426077521) ##5.2 如果消费者数超过分区数会怎么样?(顺丰 滴滴) ###当Kafka消费者的数量超过分区数时,可能会导致一些问题和挑战。以下是可能出现的情况: 1. 资源竞争: 如果消费者数量远远超过分区数量,消费者之间可能会发生资源竞争,导致处理速度变慢或者资源耗尽。这可能会导致消费者处理消息的效率降低。 2. 负载不均衡: 如果消费者数量超过分区数量,有些消费者可能会闲置,而其他消费者可能会不断地从多个分区中获取消息,导致负载不均衡。这可能会导致一些消费者过度负载,而其他消费者则相对空闲。 3. 消息处理顺序性: Kafka保证同一分区内的消息是有序的,但是如果消费者数量超过分区数量,会导致多个消费者从同一个分区中读取消息,可能会破坏消息的顺序性。 4. 增加管理复杂性: 管理大量消费者可能会增加集群的管理复杂性,包括监控、调优、重新平衡等。 ###为了解决这些问题,你可以考虑以下方法: 1. 增加分区数: 如果预计消费者数量会持续增加,可以考虑增加Kafka主题的分区数,以便更好地支持并行处理。 2. 使用消费者组: Kafka的消费者可以组成消费者组,每个消费者组内的消费者共同消费一个主题的消息。通过适当地配置消费者组,可以实现负载均衡和消息的顺序处理。 3. 动态调整消费者数量: 根据实际需求,动态地增加或减少消费者的数量,以适应消息量的变化。 4. 监控和调优: 定期监控消费者的处理速度、延迟等指标,对消费者进行调优,确保系统能够有效地处理消息。 ###总之,要根据实际情况和需求来合理配置消费者数量和分区数量,以确保 Kafka 集群的高效和稳定运行。 ##5.3 怎么保证数据的可靠投递?(陌陌 字节) [参考文档](https://cloud.tencent.com/developer/article/1722289) ##5.4 消费者的 offset 存在哪里?(字节 腾讯 陌陌) [参考文档](https://segmentfault.com/a/1190000041263079) ##5.5 如何通过 offset 定位消息?(字节) [参考文档](https://zhuanlan.zhihu.com/p/345123636) ##5.6 时间轮的原理(陌陌 顺丰) [参考文档](https://juejin.cn/post/6844904110399946766) ##5.7 kafka 写入高性能的原因,sendfile 和 mmap 原理,为什么不用 splice(滴滴) [参考文档](https://juejin.cn/post/6986572136588509214) #6. 网络 ##6.1https 原理,tls 握手需要几个 rtt?(滴滴 百度) ##6.2 浏览器访问某个网址的详细过程,四次挥手(腾讯 滴滴) ##6.3 http2 和 quic 原理(字节) ##6.4 分布式系统 ##6.5 分布式事务怎么处理(高德 陌陌) ##6.6 简述 raft 原理(陌陌) ##6.7 分布式 id 的几种实现和优缺点(滴滴) ##6.8 降级 限流 熔断实现原理(高德 陌陌) #7. 其他 ##7.1 布隆过滤器的实现原理和使用场景(滴滴) ##7.2 进程间通信有哪几种方式(腾讯) ##7.3 进程线程协程区别(滴滴 知乎) ##7.4 lvs 原理,如何保证高可用(滴滴) ##7.5 502 504 什么原因,如何处理(滴滴 百度 腾讯 顺丰) ##7.6 给你两个一模一样的玻璃球,求出 100 层楼哪一层开始玻璃球会被摔碎(腾讯) ##7.7 一致性 hash 原理,怎么解决节点少数据倾斜的问题(滴滴 陌陌) #8. 系统设计 ##8.1 设计秒杀系统,需要支持 100W 以上 QPS(滴滴) ##8.2 设计微博首页,需要拉取所有关注用户的最近 20 条微博(百度) ##8.3 抢红包算法设计(百度 滴滴) ##8.4 设计一个短链系统(百度) #9. 算法 ##9.1 leetcode 中等 很赞哦! (2) 上一篇:laravel的上线配置操作 相关文章 随机图文 swoole实现一个简单的网页版pvp(多人对战)贪吃蛇 swoole实现一个简单的网页版pvp(多人对战)贪吃蛇 php面试大全 (不定时更新) php 面试 thinkphp6 视图模板下的变量的使用 thinkphp6 视图模板下的变量的使用 Ubuntu18.04虚拟机过一段时间无法上网 Ubuntu18.04虚拟机过一段时间无法上网 文章评论 评论总数:0来说两句吧... 用户名: 验证码: 点击排行 php中关于冬夏时令切换引发的问题 php代码更新到服务器上 代码没有立即生效篇 解决方法 php面试大全 (不定时更新) php常用的 命令行 php面试题 php实现的滑动时间窗口的限流算法 最近更新 PHP面试八股文 php实现的滑动时间窗口的限流算法 php面试大全 (不定时更新) php中关于冬夏时令切换引发的问题 php代码更新到服务器上 代码没有立即生效篇 解决方法 php面试题 php常用的 命令行