面试题答案
一键面试1. 锁机制的使用
- 行锁:
- 适用场景:在抢购门票时,每个用户操作的是特定座位(假设以座位为单位售票)对应的记录。行锁可以精确锁定具体要操作的行数据,减少锁的粒度,提高并发度。例如,当用户A抢购座位1的门票时,行锁只锁住座位1对应的数据库行记录,其他用户仍然可以同时抢购其他座位的门票。
- 实现方式:在MySQL的InnoDB存储引擎下,默认使用行锁。如果执行
UPDATE seat_table SET is_sold = true WHERE seat_id = 1
这样的语句,InnoDB会自动使用行锁锁定seat_id
为1的行。
- 表锁:
- 适用场景:如果在抢购前需要先获取一些全局的票务信息,如剩余总票数等,此时可以使用表锁。因为获取全局信息时,对整个表加锁可以保证数据的一致性,防止在获取总票数过程中其他事务修改票数信息。
- 实现方式:可以使用
LOCK TABLES table_name READ/WRITE
语句来对表加读锁或写锁。例如,LOCK TABLES ticket_table READ
获取读锁,LOCK TABLES ticket_table WRITE
获取写锁。不过,表锁会锁定整个表,并发度相对较低,所以要谨慎使用,尽量缩短加锁时间。
2. 避免死锁
- 死锁原因:死锁通常发生在多个事务互相等待对方释放锁的情况下。例如,事务A持有行锁1并请求行锁2,而事务B持有行锁2并请求行锁1,就会导致死锁。
- 避免方法:
- 按照相同顺序访问资源:所有事务都按照相同的顺序(如座位ID从小到大)访问数据。这样可以避免事务之间形成循环等待的情况。例如,所有抢购事务都从座位1开始依次抢购,不会出现交叉等待锁的情况。
- 设置合理的锁超时时间:在MySQL中,可以通过
innodb_lock_wait_timeout
参数设置锁等待超时时间。如果一个事务等待锁的时间超过这个设置值,就会自动回滚,从而避免死锁。例如,设置innodb_lock_wait_timeout = 50
,即等待50秒后若还未获取到锁则事务回滚。 - 使用死锁检测:InnoDB存储引擎内置了死锁检测机制,当检测到死锁时,会自动选择一个事务进行回滚,以打破死锁。虽然这会导致部分事务失败,但可以保证系统继续运行。
3. 查询语句和索引优化
- 查询语句优化:
- 减少不必要的查询:例如,在抢购时,只查询需要的字段,而不是使用
SELECT *
。如SELECT seat_id, is_sold FROM seat_table WHERE seat_id = 1
,这样可以减少数据传输量,提高查询效率。 - 合理使用
JOIN
:如果涉及多个表(如用户表、订单表、票务表)的关联查询,确保JOIN
条件清晰且正确。例如,在获取用户已购门票信息时,SELECT * FROM user_table u JOIN order_table o ON u.user_id = o.user_id JOIN ticket_table t ON o.ticket_id = t.ticket_id
,要保证JOIN
字段上有合适的索引。
- 减少不必要的查询:例如,在抢购时,只查询需要的字段,而不是使用
- 索引优化:
- 创建适当的索引:在频繁用于
WHERE
条件、JOIN
条件的字段上创建索引。如在seat_table
表的seat_id
字段上创建索引,CREATE INDEX idx_seat_id ON seat_table(seat_id)
,这样可以加快查询速度,减少锁等待时间。 - 避免索引滥用:过多的索引会增加存储开销和更新操作的成本。因此,只在必要的字段上创建索引,并且要考虑索引的选择性,选择性越高,索引效果越好。例如,对于一个只有
0
和1
两个值的字段创建索引,其选择性较低,可能不会带来明显的性能提升。
- 创建适当的索引:在频繁用于