百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

又一批长事务,P0故障谁来背锅?

wptr33 2025-05-16 16:46 32 浏览

最近几周,发生过多起因为事务问题引起的服务报错。现象为数据库连接池连接占满,数据库连接长时间等待,最终导致请求线程hang住,服务大面积报错。这个时候,服务资源、数据库资源大量空闲,但就是进行不下去,影响是比较恶劣的。

谁来背锅?当然是架构师。因为这次所有的服务都活着,没运维什么事。

面试时,大家可能都会碰到关于事务相关的问题,升级版的可能是分布式事务的问题。在互联网行业中,一句马马虎虎的补偿事务就能蒙混过关,毕竟都是些短小精悍的借口。
但在很多企业级应用中,这行不通。我们必须直面惨淡的现实。

为什么要用长事务?

在许多业务非常复杂的后台系统,经常频繁操作DB,为了保证数据的一致性,能够在出错时回滚数据,通常会使用事务。

就拿最简单的单机数据库事务来说。

在事务操作期间,如果持续时间过长,只有等事务结束之后,DB连接才会释放,此类长时间占用DB连接的事务操作,称为长事务。一旦外部有大量请求,并发调用此操作,那么将会有大量的DB连接被持有而没有被释放掉,直到连接池爆满。

这个时候,如果有其他请求到来,那十有八九是以失败告终。

也就是说,连接资源被少数长事务操作占用。在这种情况下,即使是最简单接口查询,都不能够正常进行。

几粒老鼠屎,坏了一锅粥。

一些魔幻的反应

当你去排查这种问题的时候,可能会陷入僵局。jstack显示,多数请求其实是阻塞在tomcat的线程池上,而且是一些访问速度非常快的请求被阻塞。

比如,tomcat的200个线程,有180个阻塞在耗时不到1ms的/status接口上。

很多人就一脸懵逼。经验失灵。

jstack此时的输出结果,欺骗了我们。真正造成阻塞的,是那额外的20多个线程。

有哪些改善?

保证事务的短效是一个基本要求,包括但不限于:

应控制慢查询的调用频率,尽量减少慢查询。很多情况下,这条规则是自欺欺人的,需要业务做一些妥协。

事务内不应包含任何RPC调用,减少事务的粒度。通常,一些RPC调用,包括其他非事务资源的调用,耗时非常不可控。如果把它们也纳入事务的范围之内,势必会加剧资源的占用。事务内不应包含其他容易超时或者长时间阻塞的服务,如HTTP调用、IO操作。

次优先级服务如消息队列,不应该放在事务内,避免因为消息队列不可用引起的服务不可用。给类似消息队列的组件,设置一个合理的超时时间的非常有必要的,否则它就会一直等在那里。但即使是这样,也尽量不要把它们纳入到事务操作之内。

跨库、跨类型(如Redis),不应该放在同一事务中,可避免交叉影响。

你可以看到上面的这些描述,有些和我们所追求的数据一致性是相悖的。这不奇怪,依然是CAP原理的权衡。有些业务选择的是宁可卡死不再响应,也不能进入异常数据;有些则首先让业务运行下去,脏数据会通过补偿事务进行修正。

一切看你的选择。

设计总有人背锅,补偿总有人做出牺牲。

解决方式

那么如何来快速解决大事务造成的服务不可用问题呢?

除了扩容,其实是无解。重启大法也不见得好用。因为被阻断的请求,会以更凶猛的态势再次来袭。

你可能会想到调大连接池的大小。但在实践中得知,也不好用,大事务请求会迅速将连接池占满。

但我们可以提前进行防御。

以Spring为例,事务的使用方式大多数是使用@Transactional注解来控制的,或者是声明式事务方式。我建议以以下方式进行预防和发现:

1) 重新扫描或者Review业务代码,排查事务中是否有以上提到的各种情况。然后将除DB操作外的其他操作移动到事务之外。

2) 每个事务操作都给予足够重视,对于执行复杂度和时间复杂度不确定的事务,添加超时报警,及时发现引起的原因。

同时,还需要加强监控,辅助进行问题排查。

1) 业务可以考虑定时将数据库连接池的信息进行打印,通过看日志的方式进行初步排查。

2) 使用jstack查询执行栈,找出阻塞的点。

3) 排查并联系下游服务,找出主要原因

xjjdog倾向于使用监控快速发现问题。如图,通过连接池监控,可以看到数据库连接池连接数长时间保持在高位不释放,同时等待的线程数急剧增加。发生此种现象多数可以考虑是否是以上原因引起。

发生问题时,应及时(多次)使用jstack定位到线程的阻塞位置,然后排查下游服务是否有问题,或者是否存在慢查询。

最好的情况是服务已经进行了对代码的梳理,那么引起的原因大概率只剩下了慢查询。针对慢查询,druid数据库连接池,提供了sql的聚合,能够查看是每一类查询语句的具体执行情况。如图,短时间内SQL请求飙升,最大执行时长上升,连接池占满:

具体是哪一句SQL所引起的,一目了然。

什么是长事务

首先我们先要知道什么是长事务,顾名思义就是运行时间比较长,长时间未提交的事务,也可以称之为大事务。这类事务往往会造成大量的阻塞和锁超时,容易造成主从延迟,要尽量避免使用长事务。

下面我将演示下如何开启事务及模拟长事务:

#假设我们有一张stu_tb表,结构及数据如下
mysql> show create table stu_tb\G
*************************** 1. row ***************************
       Table: stu_tb
Create Table: CREATE TABLE `stu_tb` (
  `increment_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  `stu_id` int(11) NOT NULL COMMENT '学号',
  `stu_name` varchar(20) DEFAULT NULL COMMENT '学生姓名',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`increment_id`),
  UNIQUE KEY `uk_stu_id` (`stu_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COMMENT='测试学生表'
1 row in set (0.01 sec)

mysql> select * from stu_tb;
+--------------+--------+----------+---------------------+---------------------+
| increment_id | stu_id | stu_name | create_time         | update_time         |
+--------------+--------+----------+---------------------+---------------------+
|            1 |   1001 | from1    | 2019-09-15 14:27:34 | 2019-09-15 14:27:34 |
|            2 |   1002 | dfsfd    | 2019-09-15 14:27:34 | 2019-09-15 14:27:34 |
|            3 |   1003 | fdgfg    | 2019-09-15 14:27:34 | 2019-09-15 14:27:34 |
|            4 |   1004 | sdfsdf   | 2019-09-15 14:27:34 | 2019-09-15 14:27:34 |
|            5 |   1005 | dsfsdg   | 2019-09-15 14:27:34 | 2019-09-15 14:27:34 |
|            6 |   1006 | fgd      | 2019-09-15 14:27:34 | 2019-09-15 14:27:34 |
|            7 |   1007 | fgds     | 2019-09-15 14:27:34 | 2019-09-15 14:27:34 |
|            8 |   1008 | dgfsa    | 2019-09-15 14:27:34 | 2019-09-15 14:27:34 |
+--------------+--------+----------+---------------------+---------------------+
8 rows in set (0.00 sec)

#显式开启事务,可用begin或start transaction
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from stu_tb where stu_id = 1006 for update;
+--------------+--------+----------+---------------------+---------------------+
| increment_id | stu_id | stu_name | create_time         | update_time         |
+--------------+--------+----------+---------------------+---------------------+
|            6 |   1006 | fgd      | 2019-09-15 14:27:34 | 2019-09-15 14:27:34 |
+--------------+--------+----------+---------------------+---------------------+
1 row in set (0.01 sec)

#如果我们不及时提交上个事务,那么这个事务就变成了长事务,当其他会话要操作这条数据时,就会一直等待。
复制代码

2.如何找到长事务

遇到事务等待问题时,我们首先要做的是找到正在执行的事务。
information_schema.INNODB_TRX 表中包含了当前innodb内部正在运行的事务信息,这个表中给出了事务的开始时间,我们可以稍加运算即可得到事务的运行时间。

mysql> select t.*,to_seconds(now())-to_seconds(t.trx_started) idle_time from INFORMATION_SCHEMA.INNODB_TRX t \G
*************************** 1. row ***************************
                    trx_id: 6168
                 trx_state: RUNNING
               trx_started: 2019-09-16 11:08:27
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 3
       trx_mysql_thread_id: 11
                 trx_query: NULL
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 1
          trx_lock_structs: 3
     trx_lock_memory_bytes: 1136
           trx_rows_locked: 2
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 0
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
                 idle_time: 170复制代码

在结果中idletime是计算产生的,也是事务的持续时间。但事务的trxquery是NUL,这并不是说事务什么也没执行,一个事务可能包含多个SQL,如果SQL执行完毕就不再显示了。当前事务正在执行,innodb也不知道这个事务后续还有没有sql,啥时候会commit。 因此trx_query不能提供有意义的信息。

如果我们想看到这个事务执行过的SQL,看是否可以杀掉长事务,怎么办呢?我们可以联合其他系统表查询得到,具体查询SQL如下:

mysql> select now(),(UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(a.trx_started)) diff_sec,b.id,b.user,b.host,b.db,d.SQL_TEXT from information_schema.innodb_trx a inner join
    -> information_schema.PROCESSLIST b
    -> on a.TRX_MYSQL_THREAD_ID=b.id and b.command = 'Sleep'
    -> inner join performance_schema.threads c ON b.id = c.PROCESSLIST_ID
    -> inner join performance_schema.events_statements_current d ON d.THREAD_ID = c.THREAD_ID;
+---------------------+----------+----+------+-----------+--------+-----------------------------------------------------+
| now()               | diff_sec | id | user | host      | db     | SQL_TEXT                                            |
+---------------------+----------+----+------+-----------+--------+-----------------------------------------------------+
| 2019-09-16 14:06:26 |       54 | 17 | root | localhost | testdb | select * from stu_tb where stu_id = 1006 for update |
+---------------------+----------+----+------+-----------+--------+-----------------------------------------------------+复制代码

上述结果中diffsec和上面idletime表示意思相同,都是代表此事务持续的秒数。SQLTEXT表示该事务刚执行的SQL。但是呢,上述语句只能查到事务最后执行的SQL,我们知道,一个事务里可能包含多个SQL,那我们想查询这个未提交的事务执行过哪些SQL,是否可以满足呢,答案是结合eventsstatements_history系统表也可以满足需求。下面语句将会查询出该事务执行过的所有SQL:

mysql> SELECT
    ->   ps.id 'PROCESS ID',
    ->   ps.USER,
    ->   ps.HOST,
    ->   esh.EVENT_ID,
    ->   trx.trx_started,
    ->   esh.event_name 'EVENT NAME',
    ->   esh.sql_text 'SQL',
    ->   ps.time
    -> FROM
    ->   PERFORMANCE_SCHEMA.events_statements_history esh
    ->   JOIN PERFORMANCE_SCHEMA.threads th ON esh.thread_id = th.thread_id
    ->   JOIN information_schema.PROCESSLIST ps ON ps.id = th.processlist_id
    ->   LEFT JOIN information_schema.innodb_trx trx ON trx.trx_mysql_thread_id = ps.id
    -> WHERE
    ->   trx.trx_id IS NOT NULL
    ->   AND ps.USER != 'SYSTEM_USER'
    -> ORDER BY
    ->   esh.EVENT_ID;
+------------+------+-----------+----------+---------------------+------------------------------+-----------------------------------------------------+------+
| PROCESS ID | USER | HOST      | EVENT_ID | trx_started         | EVENT NAME                   | SQL                                                 | time |
+------------+------+-----------+----------+---------------------+------------------------------+-----------------------------------------------------+------+
|         20 | root | localhost |        1 | 2019-09-16 14:18:44 | statement/sql/select         | select @@version_comment limit 1                    |   60 |
|         20 | root | localhost |        2 | 2019-09-16 14:18:44 | statement/sql/begin          | start transaction                                   |   60 |
|         20 | root | localhost |        3 | 2019-09-16 14:18:44 | statement/sql/select         | SELECT DATABASE()                                   |   60 |
|         20 | root | localhost |        4 | 2019-09-16 14:18:44 | statement/com/Init DB        | NULL                                                |   60 |
|         20 | root | localhost |        5 | 2019-09-16 14:18:44 | statement/sql/show_databases | show databases                                      |   60 |
|         20 | root | localhost |        6 | 2019-09-16 14:18:44 | statement/sql/show_tables    | show tables                                         |   60 |
|         20 | root | localhost |        7 | 2019-09-16 14:18:44 | statement/com/Field List     | NULL                                                |   60 |
|         20 | root | localhost |        8 | 2019-09-16 14:18:44 | statement/com/Field List     | NULL                                                |   60 |
|         20 | root | localhost |        9 | 2019-09-16 14:18:44 | statement/sql/select         | select * from stu_tb                                |   60 |
|         20 | root | localhost |       10 | 2019-09-16 14:18:44 | statement/sql/select         | select * from stu_tb where stu_id = 1006 for update |   60 |
+------------+------+-----------+----------+---------------------+------------------------------+-----------------------------------------------------+------+复制代码

从上述结果中我们可以看到该事务从一开始到现在执行过的所有SQL,当我们把该事务相关信息都查询清楚后,我们就可以判定该事务是否可以杀掉,以免影响其他事务造成等待现象。

在这里稍微拓展下,长事务极易造成阻塞或者死锁现象,通常情况下我们可以首先查询 sys.innodblockwaits 视图确定有没有事务阻塞现象:

#假设一个事务执行 select * from stu_tb where stu_id = 1006 for update
#另外一个事务执行 update stu_tb set stu_name = 'wang' where stu_id = 1006

mysql> select * from sys.innodb_lock_waits\G
*************************** 1. row ***************************
                wait_started: 2019-09-16 14:34:32
                    wait_age: 00:00:03
               wait_age_secs: 3
                locked_table: `testdb`.`stu_tb`
                locked_index: uk_stu_id
                 locked_type: RECORD
              waiting_trx_id: 6178
         waiting_trx_started: 2019-09-16 14:34:32
             waiting_trx_age: 00:00:03
     waiting_trx_rows_locked: 1
   waiting_trx_rows_modified: 0
                 waiting_pid: 19
               waiting_query: update stu_tb set stu_name = 'wang' where stu_id = 1006
             waiting_lock_id: 6178:47:4:7
           waiting_lock_mode: X
             blocking_trx_id: 6177
                blocking_pid: 20
              blocking_query: NULL
            blocking_lock_id: 6177:47:4:7
          blocking_lock_mode: X
        blocking_trx_started: 2019-09-16 14:18:44
            blocking_trx_age: 00:15:51
    blocking_trx_rows_locked: 2
  blocking_trx_rows_modified: 0
     sql_kill_blocking_query: KILL QUERY 20
sql_kill_blocking_connection: KILL 20复制代码

上述结果显示出被阻塞的SQL以及锁的类型,更强大的是杀掉会话的语句也给出来了。但是并没有找到阻塞会话执行的SQL,如果我们想找出更详细的信息,可以使用下面语句:

mysql> SELECT
    ->   tmp.*,
    ->   c.SQL_Text blocking_sql_text,
    ->   p.HOST blocking_host
    -> FROM
    ->   (
    ->   SELECT
    ->     r.trx_state wating_trx_state,
    ->     r.trx_id waiting_trx_id,
    ->     r.trx_mysql_thread_Id waiting_thread,
    ->     r.trx_query waiting_query,
    ->     b.trx_state blocking_trx_state,
    ->     b.trx_id blocking_trx_id,
    ->     b.trx_mysql_thread_id blocking_thread,
    ->     b.trx_query blocking_query
    ->   FROM
    ->     information_schema.innodb_lock_waits w
    ->     INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
    ->     INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id
    ->   ) tmp,
    ->   information_schema.PROCESSLIST p,
    ->   PERFORMANCE_SCHEMA.events_statements_current c,
    ->   PERFORMANCE_SCHEMA.threads t
    -> WHERE
    ->   tmp.blocking_thread = p.id
    ->   AND t.thread_id = c.THREAD_ID
    ->   AND t.PROCESSLIST_ID = p.id \G
*************************** 1. row ***************************
  wating_trx_state: LOCK WAIT
    waiting_trx_id: 6180
    waiting_thread: 19
     waiting_query: update stu_tb set stu_name = 'wang' where stu_id = 1006
blocking_trx_state: RUNNING
   blocking_trx_id: 6177
   blocking_thread: 20
    blocking_query: NULL
 blocking_sql_text: select * from stu_tb where stu_id = 1006 for update
     blocking_host: localhost复制代码

上面结果显得更加清晰,我们可以清楚的看到阻塞端及被阻塞端事务执行的语句,有助于我们排查并确认是否可以杀掉阻塞的会话。

3.监控长事务

现实工作中我们需要监控下长事务,定义一个阈值,比如说30s 执行时间超过30s的事务即为长事务,要求记录并告警出来,提醒管理人员去处理。下面给出监控脚本,各位可以参考下,根据需求改动使用:

#!/bin/bash
# -------------------------------------------------------------------------------
# FileName:    long_trx.sh
# Describe:    monitor long transaction
# Revision:    1.0
# Date:        2019/09/16
# Author:      wang

/usr/local/mysql/bin/mysql -N -uroot -pxxxxxx -e "select now(),(UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(a.trx_started)) diff_sec,b.id,b.user,b.host,b.db,d.SQL_TEXT from information_schema.innodb_trx a inner join
information_schema.PROCESSLIST b
on a.TRX_MYSQL_THREAD_ID=b.id and b.command = 'Sleep'
inner join performance_schema.threads c ON b.id = c.PROCESSLIST_ID
inner join performance_schema.events_statements_current d ON d.THREAD_ID = c.THREAD_ID;" | while read A B C D E F G H
do
  if [ "$C" -gt 30 ]
      then
      echo $(date +"%Y-%m-%d %H:%M:%S")
      echo "processid[$D] $E@$F in db[$G] hold transaction time $C SQL:$H"
  fi
done >> /tmp/longtransaction.txt复制代码

简单说明一下,这里的-gt 30是30秒钟的意思,只要超过了30秒钟就认定是长事务,可以根据实际需要自定义。将该脚本加入定时任务中即可执行。

总结:

本文主要介绍了长事务相关内容,怎样找到长事务,怎么处理长事务,如何监控长事务。可能有些小伙伴对事务理解还不多,希望这篇文章对你有所帮助。由于本篇文章列出的查询事务相关语句较多,现总结如下:

# 查询所有正在运行的事务及运行时间
select t.*,to_seconds(now())-to_seconds(t.trx_started) idle_time from INFORMATION_SCHEMA.INNODB_TRX t \G

# 查询事务详细信息及执行的SQL
select now(),(UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(a.trx_started)) diff_sec,b.id,b.user,b.host,b.db,d.SQL_TEXT from information_schema.innodb_trx a inner join information_schema.PROCESSLIST b
on a.TRX_MYSQL_THREAD_ID=b.id and b.command = 'Sleep'
inner join performance_schema.threads c ON b.id = c.PROCESSLIST_ID
inner join performance_schema.events_statements_current d ON d.THREAD_ID = c.THREAD_ID;

# 查询事务执行过的所有历史SQL记录
SELECT
  ps.id 'PROCESS ID',
  ps.USER,
  ps.HOST,
  esh.EVENT_ID,
  trx.trx_started,
  esh.event_name 'EVENT NAME',
  esh.sql_text 'SQL',
  ps.time 
FROM
  PERFORMANCE_SCHEMA.events_statements_history esh
  JOIN PERFORMANCE_SCHEMA.threads th ON esh.thread_id = th.thread_id
  JOIN information_schema.PROCESSLIST ps ON ps.id = th.processlist_id
  LEFT JOIN information_schema.innodb_trx trx ON trx.trx_mysql_thread_id = ps.id 
WHERE
  trx.trx_id IS NOT NULL 
  AND ps.USER != 'SYSTEM_USER' 
ORDER BY
  esh.EVENT_ID;
  
 # 简单查询事务锁
 select * from sys.innodb_lock_waits\G
 
 # 查询事务锁详细信息
 SELECT
  tmp.*,
  c.SQL_Text blocking_sql_text,
  p.HOST blocking_host 
FROM
  (
  SELECT
    r.trx_state wating_trx_state,
    r.trx_id waiting_trx_id,
    r.trx_mysql_thread_Id waiting_thread,
    r.trx_query waiting_query,
    b.trx_state blocking_trx_state,
    b.trx_id blocking_trx_id,
    b.trx_mysql_thread_id blocking_thread,
    b.trx_query blocking_query 
  FROM
    information_schema.innodb_lock_waits w
    INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
    INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id 
  ) tmp,
  information_schema.PROCESSLIST p,
  PERFORMANCE_SCHEMA.events_statements_current c,
  PERFORMANCE_SCHEMA.threads t 
WHERE
  tmp.blocking_thread = p.id 
  AND t.thread_id = c.THREAD_ID 
  AND t.PROCESSLIST_ID = p.id \G


End

长事务问题的危险级别属于高危型,通常会造成严重的后果,可以通过观察监控,防范于未然。

最优的解决方式,当然是业务模型的改进。但这东西第一涉及到开发成本,第二涉及到跨部门协作。

出钱的老板,无法听懂你这些梦话。

在一些公司内部,这两者都是让人抓狂的事情,还不如痛痛快快背个锅,来得实在

相关推荐

oracle数据导入导出_oracle数据导入导出工具

关于oracle的数据导入导出,这个功能的使用场景,一般是换服务环境,把原先的oracle数据导入到另外一台oracle数据库,或者导出备份使用。只不过oracle的导入导出命令不好记忆,稍稍有点复杂...

继续学习Python中的while true/break语句

上次讲到if语句的用法,大家在微信公众号问了小编很多问题,那么小编在这几种解决一下,1.else和elif是子模块,不能单独使用2.一个if语句中可以包括很多个elif语句,但结尾只能有一个else解...

python continue和break的区别_python中break语句和continue语句的区别

python中循环语句经常会使用continue和break,那么这2者的区别是?continue是跳出本次循环,进行下一次循环;break是跳出整个循环;例如:...

简单学Python——关键字6——break和continue

Python退出循环,有break语句和continue语句两种实现方式。break语句和continue语句的区别:break语句作用是终止循环。continue语句作用是跳出本轮循环,继续下一次循...

2-1,0基础学Python之 break退出循环、 continue继续循环 多重循

用for循环或者while循环时,如果要在循环体内直接退出循环,可以使用break语句。比如计算1至100的整数和,我们用while来实现:sum=0x=1whileTrue...

Python 中 break 和 continue 傻傻分不清

大家好啊,我是大田。今天分享一下break和continue在代码中的执行效果是什么,进一步区分出二者的区别。一、continue例1:当小明3岁时不打印年龄,其余年龄正常循环打印。可以看...

python中的流程控制语句:continue、break 和 return使用方法

Python中,continue、break和return是控制流程的关键语句,用于在循环或函数中提前退出或跳过某些操作。它们的用途和区别如下:1.continue(跳过当前循环的剩余部分,进...

L017:continue和break - 教程文案

continue和break在Python中,continue和break是用于控制循环(如for和while)执行流程的关键字,它们的作用如下:1.continue:跳过当前迭代,...

作为前端开发者,你都经历过怎样的面试?

已经裸辞1个月了,最近开始投简历找工作,遇到各种各样的面试,今天分享一下。其实在职的时候也做过面试官,面试官时,感觉自己问的问题很难区分候选人的能力,最好的办法就是看看候选人的github上的代码仓库...

面试被问 const 是否不可变?这样回答才显功底

作为前端开发者,我在学习ES6特性时,总被const的"善变"搞得一头雾水——为什么用const声明的数组还能push元素?为什么基本类型赋值就会报错?直到翻遍MDN文档、对着内存图反...

2023金九银十必看前端面试题!2w字精品!

导文2023金九银十必看前端面试题!金九银十黄金期来了想要跳槽的小伙伴快来看啊CSS1.请解释CSS的盒模型是什么,并描述其组成部分。答案:CSS的盒模型是用于布局和定位元素的概念。它由内容区域...

前端面试总结_前端面试题整理

记得当时大二的时候,看到实验室的学长学姐忙于各种春招,有些收获了大厂offer,有些还在苦苦面试,其实那时候的心里还蛮忐忑的,不知道自己大三的时候会是什么样的一个水平,所以从19年的寒假放完,大二下学...

由浅入深,66条JavaScript面试知识点(七)

作者:JakeZhang转发链接:https://juejin.im/post/5ef8377f6fb9a07e693a6061目录由浅入深,66条JavaScript面试知识点(一)由浅入深,66...

2024前端面试真题之—VUE篇_前端面试题vue2020及答案

添加图片注释,不超过140字(可选)1.vue的生命周期有哪些及每个生命周期做了什么?beforeCreate是newVue()之后触发的第一个钩子,在当前阶段data、methods、com...

今年最常见的前端面试题,你会做几道?

在面试或招聘前端开发人员时,期望、现实和需求之间总是存在着巨大差距。面试其实是一个交流想法的地方,挑战人们的思考方式,并客观地分析给定的问题。可以通过面试了解人们如何做出决策,了解一个人对技术和解决问...