1. Mysql有哪些存储引擎,默认的存储引擎是什么
2. 什么是索引,有哪些作用,
在数据库中,索引(Index) 是一种用于 加速数据检索 的数据结构。它类似于书籍的目录,可以帮助数据库快速定位到符合条件的数据行,而不必扫描整个表。
3. 一个表中应该有多少条索引。什么样的情况不应该添加索引
一、一个表中应该有多少条索引
主键自动创建索引:每个表应有且仅有一个主键索引(聚簇索引)。
频繁查询的字段:为经常出现在
WHERE、JOIN、ORDER BY中的字段添加索引。外键字段:建议为外键字段添加索引以提升连接效率。
联合索引优化多条件查询:如
(user_id, create_time)。
二、哪些情况不应该添加索引?
以下情况添加索引不仅无效,还可能影响性能:
1. 数据区分度低的字段
如性别(男/女)、状态(0/1)、枚举值较少的字段。
索引命中率低,数据库可能会选择忽略索引直接扫描全表。
4. 尝试为之前的表中插入10万条随机数据
import random
import mysql.connector
from datetime import datetime, timedelta
# 生成随机姓名
def random_name():
first_names = ['张', '李', '王', '刘', '陈', '杨', '赵', '黄', '周', '吴']
last_names = ['伟', '芳', '娜', '敏', '强', '磊', '军', '洋', '勇', '娟']
return random.choice(first_names) + random.choice(last_names)
# 生成指定范围内的随机日期
def random_date(start, end):
delta = end - start
return start + timedelta(days=random.randint(0, delta.days))
# 连接数据库
conn = mysql.connector.connect(
host='localhost',
user='root',
password='your_password',
database='your_database'
)
cursor = conn.cursor()
# 准备插入语句
insert_sql = "INSERT INTO users (name, birthday, gender) VALUES (%s, %s, %s)"
batch_size = 1000
total_records = 100000
start_date = datetime.strptime('1980-01-01', '%Y-%m-%d')
end_date = datetime.strptime('2005-12-31', '%Y-%m-%d')
try:
for i in range(0, total_records, batch_size):
batch_data = []
for _ in range(batch_size):
name = random_name()
birthday = random_date(start_date, end_date).date()
gender = random.choice(['男', '女'])
batch_data.append((name, birthday, gender))
cursor.executemany(insert_sql, batch_data)
conn.commit() # 每批提交一次事务
print(f"Inserted {i + batch_size} records...")
print("Data insertion completed.")
except Exception as e:
print(f"Error occurred: {e}")
finally:
cursor.close()
conn.close()
5. 查询年龄最大的10个人,记录查询时间,之后为生日字段添加索引,再次查询,对比有索引和无索引,快了多长时间
import time
import mysql.connector
# 数据库配置信息,请根据实际情况修改
config = {
'user': 'root',
'password': '911209787',
'host': '192.168.75.130',
'database': 'test_db',
'raise_on_warnings': True
}
def execute_query(query):
# 连接数据库
cnx = mysql.connector.connect(**config)
cursor = cnx.cursor()
start_time = time.time() # 记录开始时间
cursor.execute(query) # 执行SQL查询
result = cursor.fetchall()# 获取所有结果
end_time = time.time() # 记录结束时间
elapsed_time = end_time - start_time # 计算耗时
cursor.close()
cnx.close()
return result, elapsed_time
def main():
# 查询年龄最大的10个人(即出生日期最早的10个人)
query = "SELECT * FROM users ORDER BY birthday ASC LIMIT 10;"
result, elapsed_time = execute_query(query)
print(f"查询结果: {result}")
print(f"无索引时查询耗时: {elapsed_time:.6f} 秒")
if __name__ == "__main__":
main()
添加索引
ALTER TABLE users ADD INDEX idx_birthday (birthday);
6. 什么是事务,事务有哪些特性
什么是事务?
在数据库管理系统中,事务(Transaction) 是一个逻辑操作单元,它包含一组数据库操作语句。这些操作要么全部成功执行,要么全部失败回滚,形成一个不可分割的工作单位。事务确保数据从一种一致状态转换到另一种一致状态。
例如,在银行转账场景中:
A 账户减少 100 元;
B 账户增加 100 元。
这两个操作必须作为一个事务来处理,以防止其中一条操作失败导致数据不一致。
事务的四大特性(ACID)
事务具有四个核心特性,通常称为 ACID 特性:
事务控制命令(SQL)
7. 事务如何标记开始,执行和回滚
数据库操作中,事务的生命周期通常包括以下几个阶段:开始、执行、提交或回滚。以下是详细的说明和示例:
1. 事务的开始
事务的开始可以通过显式声明来启动,也可以由数据库自动隐式开始。
显式开始事务
使用 START TRANSACTION 或 BEGIN 命令来手动开启事务。
START TRANSACTION;
-- 或者
BEGIN;隐式开始事务
当你执行第一个数据修改语句(如 INSERT, UPDATE, DELETE)时,数据库会自动开启一个事务(取决于数据库配置和是否启用自动提交模式)。
2. 事务的执行
在事务开始后,你可以执行多个 SQL 操作,这些操作属于同一个事务,直到你明确提交 (COMMIT) 或回滚 (ROLLBACK)。
示例:银行转账场景
START TRANSACTION;
-- A 账户减少 100 元
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
-- B 账户增加 100 元
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2;在这个例子中,两个 UPDATE 操作都在同一个事务中。
3. 事务的提交
如果所有操作都成功完成,并且你确认希望将更改永久保存到数据库中,则可以使用 COMMIT 提交事务。
COMMIT;一旦提交,所有的更改都会被写入数据库,并对其他事务可见。
4. 事务的回滚
如果在事务执行过程中发生错误,或者你决定放弃当前的更改,可以使用 ROLLBACK 回滚事务。
ROLLBACK;这将撤销事务中的所有更改,恢复到事务开始前的状态。
完整示例:
-- 开始事务
START TRANSACTION;
-- 执行操作
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2;
-- 检查是否有错误(可选)
-- 如果一切正常,提交事务
COMMIT;
-- 如果出错,回滚事务
-- ROLLBACK;
8. 什么是数据一致性,在关系型数据库中,数据一致性是否是最重要的
什么是数据一致性?
数据一致性(Data Consistency) 是指在数据库系统中,数据在整个生命周期内始终保持合法、有效和可预测的状态。这意味着:
数据库中的所有数据都符合预定义的业务规则、约束和逻辑;
数据不会因为并发操作、系统故障或错误操作而处于不一致状态;
所有事务执行前后,数据库从一个一致状态转换到另一个一致状态。
在关系型数据库中,数据一致性是构建可靠系统的基础,因此通常是最重要的目标之一。不过,在特定场景(如高并发、大数据量分析)中,可以根据需求在一致性与其他特性之间进行权衡。
9. 什么是锁,是为了解决什么问题,达到什么效果
锁(Lock) 是一种 并发控制机制,用于在多个用户或进程同时访问共享资源时,防止数据不一致、冲突或破坏。
在数据库和操作系统中,锁可以确保:
同一时间只有一个线程/事务能修改某个资源;
避免脏读、不可重复读、幻读等并发问题;
维护数据的完整性和一致性。
锁是为了解决什么问题?
1. 并发访问导致的数据不一致
当多个事务同时操作同一数据时,可能出现以下问题:
2. 资源竞争(Race Condition)
多个线程/进程同时尝试修改同一个资源(如计数器、库存数量),可能导致最终值错误。
3. 死锁(Deadlock)
两个或多个事务相互等待对方释放资源,导致系统停滞。
锁能达到什么效果?
锁是数据库和操作系统中实现并发安全和数据一致性的核心机制。通过合理使用锁,可以有效防止并发问题、维护系统稳定,并保障业务逻辑的正确执行。
10. 什么是表锁,行锁。什么是共享锁,排它锁。
锁按锁定对象划分可以划分为以下三种:
锁按访问权限划分可以分为以下三种:
11. 什么是悲观锁,什么是乐观锁,什么是意向锁
一、悲观锁(Pessimistic Lock)
定义:
悲观锁 假设并发冲突 经常发生,因此在整个数据操作过程中都会加锁,防止其他事务访问或修改。
加锁方式:
显式使用
SELECT ... FOR UPDATE或SELECT ... LOCK IN SHARE MODE在更新前锁定资源,确保整个事务期间只有当前事务可以操作
适用场景:
写操作频繁的业务
高并发环境下对数据一致性要求高
数据竞争激烈(如库存扣减、订单处理)
二、乐观锁(Optimistic Lock)
定义:
乐观锁 假设并发冲突 不常发生,只在提交更新时检查版本号或时间戳,如果发现冲突则拒绝执行更新并提示用户重试。
实现方式:
使用版本号字段(
version)使用时间戳字段(
timestamp)检查字段值是否变化,再决定是否允许更新
适用场景:
读多写少的系统(如论坛、文档协作编辑)
网络环境复杂、长事务操作
不希望长时间持有数据库锁的情况
三、意向锁(Intention Lock)
定义:
意向锁 是一种表级锁,用于表示事务想要在某些行上加锁。它本身并不直接限制其他事务的操作,而是告诉数据库“我准备在某一行上加锁”。
类型:
工作机制:
当事务对某行加锁时,InnoDB 自动为对应的表加上意向锁;
其他事务看到意向锁后,就知道后续可能有行锁操作,从而避免冲突。
12. 什么是死锁,死锁的出现需要满足哪些条件,出现死锁后如何处理,如何预防
一、什么是死锁?
死锁(Deadlock) 是指两个或多个事务在执行过程中,因争夺资源而造成的一种 互相等待的僵局。每个事务都持有部分资源,并等待其他事务释放它所需的资源,导致所有事务都无法继续执行。
二、死锁出现需要满足哪些条件?(四个必要条件)
死锁的发生必须同时满足以下 四个条件:
三、出现死锁后如何处理?
1. 数据库自动检测与回滚
大多数数据库系统(如 MySQL InnoDB、Oracle、PostgreSQL)会定期运行死锁检测机制;
发现死锁后,会选择一个代价最小的事务进行 回滚(Rollback);
回滚事务后,其他事务可以继续执行。
2. 应用层重试机制
在代码中捕获死锁异常(如 MySQL 返回
Deadlock found when trying to get lock);自动重试事务(建议设置最大重试次数和退避时间);
四、如何预防死锁?
1. 统一访问顺序
所有事务按相同的顺序访问表和字段;
避免不同事务以不同顺序加锁,减少循环依赖。
2. 缩短事务生命周期
尽量减少事务执行时间和锁持有时间;
提交或回滚越快,锁释放越快,降低冲突概率。
3. 合理设置索引
索引缺失可能导致锁升级为表锁;
合理使用索引,避免全表扫描带来的锁竞争。
4. 批量操作优先
将多个 SQL 操作合并为一个事务,减少多次交互带来的锁竞争;
减少网络延迟对事务的影响。
5. 设置合理的超时时间
设置事务等待锁的最大时间(InnoDB 默认 50 秒):
SET innodb_lock_wait_timeout = 30; -- 设置等待锁最多 30 秒6. 使用乐观锁替代悲观锁
对于写冲突较少的场景,使用乐观锁(版本号控制)替代行锁;
避免长时间持有锁资源。
总结
13. 什么是隐式加锁,什么是显式加锁,显式加锁有什么注意事项
一、什么是隐式加锁?
隐式加锁(Implicit Locking) 是指数据库在执行某些操作时 自动为资源加上锁,开发者无需手动干预。
常见隐式加锁场景(MySQL InnoDB):
二、什么是显式加锁?
显式加锁(Explicit Locking) 是指开发者通过 SQL 显式地为某个表或记录加锁,控制并发访问。
显式加锁命令示例(MySQL):
表锁:
LOCK TABLE users READ; -- 加共享表锁
LOCK TABLE users WRITE; -- 加排他表锁
UNLOCK TABLES; -- 解锁行锁:
START TRANSACTION;
SELECT * FROM inventory WHERE product_id = 1001 FOR UPDATE; -- 排他锁
COMMIT;三、显式加锁有什么注意事项?
✅ 使用建议
四、显式加锁的常见问题与规避策略
14. 如果没有锁,会出现哪些问题?什么是脏读、幻读、不可重复读、丢失更新
在并发数据库操作中,如果没有锁机制,多个事务同时访问和修改数据时会出现一系列一致性问题,最常见的包括:
15. 事务的4个隔离级别分别是什么,每个隔离级别能防止哪些并发问题
事务的隔离级别用于控制事务之间的可见性和并发行为,确保在多事务同时执行时数据的一致性。SQL 标准定义了四个事务隔离级别,每个级别能防止不同的并发问题。
事务的 4 个隔离级别
每个隔离级别能防止哪些并发问题?
1. READ UNCOMMITTED(读未提交)
特点:最低的隔离级别,事务可以读取其他事务尚未提交的数据。
适用场景:几乎不用于生产环境。
可出现的问题:
脏读
不可重复读
幻读
写偏移
2. READ COMMITTED(读已提交)
特点:事务只能看到已经提交的数据变更。
适用场景:适用于对一致性要求不高、但希望提高并发性能的系统。
可出现的问题:
脏读
不可重复读
幻读
写偏移
3. REPEATABLE READ(可重复读)
特点:事务在整个执行期间多次读取同一行的结果保持一致。
适用场景:MySQL 默认使用的隔离级别。
可出现的问题:
脏读
不可重复读
幻读 (InnoDB 使用间隙锁解决此问题)
写偏移 (取决于数据库实现)
4. SERIALIZABLE(串行化)
特点:所有事务串行执行,避免一切并发问题。
适用场景:对一致性要求极高,且并发性能不是主要考虑因素的金融系统或审计系统。
可出现的问题:
脏读
不可重复读
幻读
写偏移
隔离级别与锁机制的关系
16. 如何查看当前数据库的锁等待和死锁信息
一、查看当前锁等待信息
方法:查询 information_schema 中的 INNODB_LOCKS 和 INNODB_LOCK_WAITS
SELECT
r.trx_id AS waiting_trx_id,
r.trx_mysql_thread_id AS waiting_thread,
r.trx_query AS waiting_query,
b.trx_id AS blocking_trx_id,
b.trx_mysql_thread_id AS blocking_thread,
b.trx_query AS blocking_query
FROM
information_schema.INNODB_LOCK_WAITS w
JOIN
information_schema.INNODB_TRX b ON b.trx_id = w.blocking_trx_id
JOIN
information_schema.INNODB_TRX r ON r.trx_id = w.requesting_trx_id;输出字段说明:
二、查看最近一次死锁信息
方法:使用 SHOW ENGINE INNODB STATUS\G
SHOW ENGINE INNODB STATUS\G