面试题答案
一键面试索引设计与优化
- MySQL索引设计
- 针对用户表:
- 如果查询主要基于用户ID来获取基本信息,为用户表的
user_id
字段创建主键索引。例如,若使用CREATE TABLE user_table (user_id INT PRIMARY KEY, username VARCHAR(255), ...)
语句创建表,user_id
就自动成为主键索引。 - 若经常根据某些特定字段(如
age
、city
等)筛选用户,对这些字段创建单字段索引。例如CREATE INDEX idx_age ON user_table(age);
。 - 若查询条件涉及多个字段组合(如
WHERE age > 30 AND city = 'New York'
),创建联合索引CREATE INDEX idx_age_city ON user_table(age, city);
,注意索引字段顺序,把选择性高(基数大)的字段放在前面。
- 如果查询主要基于用户ID来获取基本信息,为用户表的
- 针对用户表:
- PostgreSQL索引设计
- 针对消费记录表:
- 若通过用户ID关联获取消费记录,为消费记录表的
user_id
字段创建索引。可以使用CREATE INDEX idx_user_id ON consumption_table(user_id);
。 - 如果消费记录查询还经常基于消费时间范围(如最近一个月的消费记录),对
consumption_time
字段创建索引,如CREATE INDEX idx_consumption_time ON consumption_table(consumption_time);
。 - 对于复杂查询,例如按用户ID和消费时间范围同时查询,创建联合索引
CREATE INDEX idx_user_id_time ON consumption_table(user_id, consumption_time);
。
- 若通过用户ID关联获取消费记录,为消费记录表的
- PostgreSQL特定优化:PostgreSQL支持多种索引类型,如B - Tree、Hash、GiST等。对于等值查询,Hash索引可能更高效;对于范围查询,B - Tree索引通常是较好选择。例如,如果查询主要是判断消费金额是否等于某个值,可以考虑使用Hash索引
CREATE INDEX idx_amount_hash ON consumption_table USING hash(amount);
。
- 针对消费记录表:
跨库事务中索引的注意事项
- 索引一致性:确保在跨库事务涉及的表上的索引都能有效支持事务中的查询操作。例如,在一个跨库事务中,从MySQL用户表获取用户ID,再根据该ID从PostgreSQL消费记录表获取消费记录,两个表上的
user_id
索引都要正常工作,否则可能导致事务执行缓慢或失败。 - 索引更新与事务原子性:在跨库事务中,如果对数据进行修改并涉及索引更新,要保证整个事务的原子性。例如,在MySQL中更新用户信息(可能影响索引),同时在PostgreSQL中插入新的消费记录(也可能影响索引),要么所有操作都成功,要么都回滚,防止数据不一致。
使用Knex.js实现高效跨数据库查询
- 初始化Knex.js:
- 安装Knex.js及相应数据库驱动(
mysql2
和pg
):npm install knex mysql2 pg
。 - 配置Knex.js连接不同数据库,例如:
- 安装Knex.js及相应数据库驱动(
const knex = require('knex')({
client:'mysql2',
connection: {
host: '127.0.0.1',
user: 'root',
password: 'password',
database: 'user_db'
}
});
const knexPg = require('knex')({
client: 'pg',
connection: {
host: '127.0.0.1',
user: 'postgres',
password: 'password',
database: 'consumption_db'
}
});
- 跨数据库查询实现:
- 先从MySQL获取用户基本信息:
knex('user_table')
.select('user_id', 'username')
.where('user_id', 1)
.then((users) => {
if (users.length > 0) {
const userId = users[0].user_id;
return knexPg('consumption_table')
.select('amount', 'consumption_time')
.where('user_id', userId);
}
return [];
})
.then((consumptions) => {
console.log('User consumptions:', consumptions);
})
.catch((error) => {
console.error('Error:', error);
});
- 处理跨库事务(若有需求):
- Knex.js本身没有直接的跨库事务支持,但可以借助外部库(如
knex - multi - transaction
)或手动管理事务。例如手动管理事务:
- Knex.js本身没有直接的跨库事务支持,但可以借助外部库(如
knex.transaction((trx) => {
// MySQL操作
trx('user_table')
.update({ username: 'new_username' })
.where('user_id', 1)
.then(() => {
// PostgreSQL操作
return knexPg.transaction((pgTrx) => {
return pgTrx('consumption_table')
.insert({ user_id: 1, amount: 100, consumption_time: new Date() })
.then(() => {
pgTrx.commit();
})
.catch((pgError) => {
pgTrx.rollback();
throw pgError;
});
});
})
.then(() => {
trx.commit();
})
.catch((error) => {
trx.rollback();
console.error('Transaction error:', error);
});
});
在上述代码中,通过手动管理事务,先在MySQL事务中更新用户信息,成功后在PostgreSQL事务中插入消费记录,任何一步失败都会回滚整个操作。