|
MySQL里跨库查询其实就一句话:用库名.表名直接引用。跨库查比如SELECT * FROM db1.users u JOIN db2.orders o ON u.id = o.user_id,询语只要这两个库在同一台MySQL实例上,跨库查这条语句立刻能跑。询语没有额外配置,跨库查不需要什么中间件,询语权限对了就行。跨库查 问题在于很多人把"跨库"和"跨实例"混为一谈。询语跨库是跨库查同一个MySQL进程里不同database之间的操作,跨实例则是询语两台甚至多台服务器之间的数据交互。前者MySQL原生支持,跨库查后者得靠FEDERATED引擎、询语中间件或者应用层拼装。跨库查搞混了就会在错误的询语方向上折腾半天。 权限是跨库查第一个坎 默认情况下,一个用户只有自己默认库的权限。你写db2.orders,MySQL会检查当前用户对db2有没有SELECT权限。没有的话直接报ERROR 1142。解决办法也简单: GRANT SELECT ON db2.* TO 'myuser'@'%'; FLUSH PRIVILEGES; 我见过一个案例,开发在本地测试环境跑通了跨库JOIN,部署到生产直接挂掉。原因就是生产环境的账号只授了单库权限。这种事排查起来不复杂,但第一次遇到容易懵——SQL语法没问题,表也存在,就是报权限错误。 JOIN的性能陷阱 跨库JOIN和同库JOIN在执行层面没有本质区别,优化器一样会选驱动表、走索引。但有个细节容易被忽略:如果两个库的字符集不一致,JOIN字段的隐式转换会让索引失效。比如db1是utf8mb4,db2是latin1,关联字段又都是VARCHAR,MySQL会在比较时做字符集转换,EXPLAIN里type直接变成ALL。 还有个实际问题:跨库查询的锁范围。InnoDB的行锁是基于索引的,跨库JOIN时如果驱动表的索引选择不当,可能锁住比预期更多的行。两个库的表如果有写入压力,死锁概率会上升。这不是跨库特有的问题,但跨库场景下更容易触发,因为DBA往往只关注单库的锁情况。 事务怎么办 同一实例下的跨库操作,事务是统一的。START TRANSACTION之后操作db1和db2的表,要么一起提交要么一起回滚。MySQL的binlog也是按实例写的,不按库拆分。所以别担心"跨库事务不生效"这种说法,那是跨实例才有的问题。 但有个坑:如果你用了SET autocommit=0又忘了提交,跨库查询会一直持有MDL元数据锁,DDL操作全被堵住。这个和跨库无关,纯粹是事务管理的问题,只是跨库时涉及的表更多,影响面更大。 什么时候不该用跨库查询 如果两个库的数据量都很大,跨库JOIN的临时表可能撑爆tmp_table_size。我建议超过百万级别的表,先把关联字段的数据捞到应用层做hash join,或者用汇总表预计算。MySQL的优化器在跨库场景下并不会比同库更聪明,它看到的只是两个普通的表。 另外,分库分表中间件(ShardingSphere、MyCat这些)对跨库查询的支持有限。它们通常要求JOIN字段包含分片键,否则会做全路由广播,性能极差。如果你的架构已经上了分片,跨库查询基本等于给自己挖坑。 说到底,MySQL跨库查询是个"能用但别滥用"的功能。临时取数、小表关联、后台报表,随便用。核心业务链路里频繁跨库JOIN,迟早要还技术债。 |