面试题答案
一键面试数据库配置层面
- 调整缓存:
- 查询缓存:开启MySQL查询缓存,对于相同的查询,MySQL可以直接从缓存中返回结果,减少数据库的实际查询压力。但要注意,查询缓存对数据一致性敏感,数据更新时缓存会失效。可以通过修改
my.cnf
配置文件中的query_cache_type
和query_cache_size
参数来配置查询缓存。例如:
[mysqld] query_cache_type = 1 query_cache_size = 64M
- InnoDB Buffer Pool:InnoDB存储引擎的缓冲池,用于缓存数据和索引。增大缓冲池大小可以减少磁盘I/O,提高查询性能。同样在
my.cnf
文件中配置,如:
[mysqld] innodb_buffer_pool_size = 2G
- 查询缓存:开启MySQL查询缓存,对于相同的查询,MySQL可以直接从缓存中返回结果,减少数据库的实际查询压力。但要注意,查询缓存对数据一致性敏感,数据更新时缓存会失效。可以通过修改
- 优化线程参数:
- 线程池:使用线程池来管理连接线程,减少线程创建和销毁的开销。可以通过
thread_pool_size
参数设置线程池大小,例如:
[mysqld] thread_pool_size = 64
- 最大连接数:合理设置
max_connections
参数,避免过多连接耗尽系统资源。同时要结合服务器硬件资源,如内存等进行调整。例如:
[mysqld] max_connections = 200
- 线程池:使用线程池来管理连接线程,减少线程创建和销毁的开销。可以通过
- 索引优化:
- 添加合适索引:分析业务查询语句,对经常用于
WHERE
、JOIN
等条件的字段添加索引。但要注意索引过多也会增加写入开销,因为每次数据更新时索引也需要更新。例如对于SELECT * FROM users WHERE username = 'test'
这样的查询,可以在username
字段上添加索引:
CREATE INDEX idx_username ON users (username);
- 复合索引:对于多个字段联合查询的情况,使用复合索引。例如
SELECT * FROM orders WHERE user_id = 1 AND order_date > '2023 - 01 - 01'
,可以创建复合索引CREATE INDEX idx_user_date ON orders (user_id, order_date);
- 添加合适索引:分析业务查询语句,对经常用于
Python代码层面
- 连接池使用:
- 使用数据库连接池库:如
DBUtils
,它可以管理和复用数据库连接,避免频繁创建和销毁连接。示例代码如下:
from dbutils.pooled_db import PooledDB import mysql.connector pool = PooledDB( creator = mysql.connector, host='localhost', user='root', password='password', database='test_db', autocommit=True, maxconnections = 100 ) def get_connection(): return pool.connection()
- 在Web应用中使用连接池:在处理请求时,从连接池获取连接,处理完后归还连接。例如在Flask应用中:
from flask import Flask app = Flask(__name__) @app.route('/') def index(): conn = get_connection() try: cursor = conn.cursor() cursor.execute('SELECT * FROM users') result = cursor.fetchall() return str(result) finally: conn.close()
- 使用数据库连接池库:如
- 事务优化:
- 合理控制事务范围:避免事务过大,将事务分解为多个小事务。例如,在涉及多个数据库操作时,如果某些操作可以独立成小事务,就分别处理。
conn = get_connection() try: cursor = conn.cursor() cursor.execute('INSERT INTO table1 (column1) VALUES (%s)', ('value1',)) conn.commit() cursor.execute('INSERT INTO table2 (column2) VALUES (%s)', ('value2',)) conn.commit() except Exception as e: conn.rollback() raise e finally: conn.close()
- 使用批量操作:在插入或更新数据时,尽量使用批量操作。例如
executemany
方法,减少数据库交互次数。
data = [('value1', 'value2'), ('value3', 'value4')] conn = get_connection() try: cursor = conn.cursor() cursor.executemany('INSERT INTO table (col1, col2) VALUES (%s, %s)', data) conn.commit() except Exception as e: conn.rollback() raise e finally: conn.close()
架构设计层面
- 读写分离:
- 主从复制:配置MySQL主从复制,主库负责写操作,从库负责读操作。在Python代码中,根据操作类型选择连接主库或从库。例如,使用
Flask - SQLAlchemy
实现读写分离可以通过配置不同的数据库URI来实现:
from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] ='mysql://root:password@master - host:3306/main_db' app.config['SQLALCHEMY_BINDS'] = { 'slave':'mysql://root:password@slave - host:3306/main_db' } db = SQLAlchemy(app) # 读操作从从库读取 @app.route('/read') def read(): with db.using_bind('slave') as session: result = session.query(Model).all() return str(result) # 写操作从主库写入 @app.route('/write') def write(): new_obj = Model() db.session.add(new_obj) db.session.commit() return 'Write success'
- 主从复制:配置MySQL主从复制,主库负责写操作,从库负责读操作。在Python代码中,根据操作类型选择连接主库或从库。例如,使用
- 缓存中间件:
- Redis缓存:在Web应用和数据库之间添加Redis缓存。对于频繁读取且不经常变化的数据,先从Redis缓存中获取,如果缓存中没有则查询数据库并将结果存入缓存。例如,在Flask应用中使用
Flask - Caching
扩展结合Redis:
from flask import Flask from flask_caching import Cache app = Flask(__name__) cache = Cache(app, config = {'CACHE_TYPE':'redis', 'CACHE_REDIS_HOST': 'localhost', 'CACHE_REDIS_PORT': 6379}) @app.route('/cached - data') @cache.cached(timeout = 60) def get_cached_data(): conn = get_connection() try: cursor = conn.cursor() cursor.execute('SELECT * FROM users') result = cursor.fetchall() return str(result) finally: conn.close()
- Redis缓存:在Web应用和数据库之间添加Redis缓存。对于频繁读取且不经常变化的数据,先从Redis缓存中获取,如果缓存中没有则查询数据库并将结果存入缓存。例如,在Flask应用中使用
- 分布式架构:
- 水平拆分:按照业务维度或数据范围对数据库进行水平拆分,将不同的数据分布到多个数据库实例上。例如,根据用户ID的哈希值将用户数据分布到不同的数据库,减少单个数据库的压力。
- 负载均衡:使用负载均衡器(如Nginx)来分发数据库请求,将请求均匀分配到多个数据库实例上,提高系统的整体性能和可用性。