一文詳解MySQL實現(xiàn)分庫分表的詳細(xì)步驟
一、分庫分表是什么?——數(shù)據(jù)庫的“甜蜜煩惱”
把你的MySQL數(shù)據(jù)庫想象成是個小單身公寓(單庫單表):
- 剛開始住進(jìn)去時,東西不多,找啥都方便
- 后來你結(jié)婚了(用戶量增加),生了娃(數(shù)據(jù)量暴增),還養(yǎng)了條二哈(業(yè)務(wù)復(fù)雜了)
- 現(xiàn)在全家擠在小公寓里,每天早上一家人搶廁所(數(shù)據(jù)庫鎖競爭),找雙襪子要翻遍全家(全表掃描)
- 鄰居天天投訴你家太吵(性能影響其他服務(wù))
這時候你需要: 分庫:買下整棟樓!把不同的家人安排在不同樓層(業(yè)務(wù)垂直拆分) 分表:把每個房間再隔成小隔間!把襪子、內(nèi)褲、外套分開放(數(shù)據(jù)水平拆分)
二、詳細(xì)實施步驟——從“蝸居”到“豪宅”的裝修指南
第1步:設(shè)計藍(lán)圖——想清楚再動手
-- 先看看現(xiàn)在的“房子”有多大
SELECT
TABLE_SCHEMA as '數(shù)據(jù)庫',
TABLE_NAME as '表名',
ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) as '大小(MB)',
TABLE_ROWS as '行數(shù)'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = '你的數(shù)據(jù)庫名'
ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC;
-- 思考人生三連問:
-- 1. 按業(yè)務(wù)分?(用戶相關(guān)、訂單相關(guān)、商品相關(guān))
-- 2. 按時間分?(2024訂單、2025訂單)
-- 3. 按地域分?(北京用戶、上海用戶)
第2步:垂直分庫——讓專業(yè)的人住專業(yè)的樓層
-- 原來都擠在一個庫里
CREATE DATABASE single_apartment;
-- 現(xiàn)在買棟樓,每層一個專業(yè)戶
CREATE DATABASE user_villa; -- 用戶專屬樓層
CREATE DATABASE order_mansion; -- 訂單豪華層
CREATE DATABASE product_tower; -- 商品展示層
-- 用戶表搬到用戶樓層
CREATE TABLE user_villa.user_info (
user_id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
email VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
-- 用戶相關(guān)的其他字段...
);
-- 訂單表搬到訂單樓層
CREATE TABLE order_mansion.order_info (
order_id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
amount DECIMAL(10, 2),
status TINYINT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
-- 訂單相關(guān)的其他字段...
);
第3步:水平分表——每個房間都裝上帝視角衣柜
方法一:按范圍分表(適合時間序列)
-- 訂單表按月份拆分,再也不怕找去年的訂單像考古了
-- 2024年訂單表
CREATE TABLE order_mansion.order_202401 (
order_id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
-- ... 其他字段
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user_id (user_id),
INDEX idx_created_at (created_at)
) PARTITION BY RANGE (MONTH(created_at)) (
PARTITION p1 VALUES LESS THAN (2),
PARTITION p2 VALUES LESS THAN (3),
-- ... 更多分區(qū)
);
-- 2024年2月訂單表
CREATE TABLE order_mansion.order_202402 (
-- 結(jié)構(gòu)同上
);
方法二:按哈希分表(均勻分布)
-- 用戶表按ID取模分表,16張表夠不夠?
-- 先創(chuàng)建用戶表模板
DELIMITER $$
CREATE PROCEDURE create_user_tables()
BEGIN
DECLARE i INT DEFAULT 0;
WHILE i < 16 DO
SET @table_name = CONCAT('user_villa.user_info_', LPAD(i, 2, '0'));
SET @sql = CONCAT('
CREATE TABLE IF NOT EXISTS ', @table_name, ' (
user_id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
email VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET i = i + 1;
END WHILE;
END$$
DELIMITER ;
-- 執(zhí)行創(chuàng)建
CALL create_user_tables();
第4步:路由策略——給每個數(shù)據(jù)配個“導(dǎo)航系統(tǒng)”
// Java代碼示例:數(shù)據(jù)路由導(dǎo)航
public class ShardingNavigator {
// 用戶表路由:根據(jù)user_id決定去哪個表
public static String getUserTableName(long userId) {
int tableIndex = (int) (userId % 16);
return String.format("user_info_%02d", tableIndex);
}
// 訂單表路由:根據(jù)時間決定去哪個表
public static String getOrderTableName(Date orderTime) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
return "order_" + sdf.format(orderTime);
}
// 組合查詢:跨表查詢就像組織家庭聚會
public List<Order> getUserOrders(long userId, Date startTime, Date endTime) {
List<Order> allOrders = new ArrayList<>();
Calendar calendar = Calendar.getInstance();
calendar.setTime(startTime);
// 遍歷時間范圍內(nèi)的所有月份表
while (calendar.getTime().before(endTime)) {
String tableName = getOrderTableName(calendar.getTime());
String sql = "SELECT * FROM " + tableName + " WHERE user_id = ?";
// 執(zhí)行查詢并添加到結(jié)果集
// ...
calendar.add(Calendar.MONTH, 1);
}
return allOrders;
}
}
第5步:中間件配置——請個“萬能管家”
# ShardingSphere配置文件示例(YAML格式)
# 這位管家知道所有房間在哪
dataSources:
ds0:
url: jdbc:mysql://localhost:3306/user_villa?useSSL=false
username: root
password: your_password
ds1:
url: jdbc:mysql://localhost:3307/order_mansion?useSSL=false
username: root
password: your_password
shardingRule:
tables:
user_info:
actualDataNodes: ds0.user_info_${0..15}
tableStrategy:
inline:
shardingColumn: user_id
algorithmExpression: user_info_${user_id % 16}
order_info:
actualDataNodes: ds1.order_${2024..2025}${1..12}
tableStrategy:
standard:
shardingColumn: created_at
preciseAlgorithmClassName: com.example.TimeShardingAlgorithm
bindingTables:
- user_info,order_info
第6步:數(shù)據(jù)遷移——搬家不能丟東西
-- 第一步:先抄家(備份)
CREATE TABLE order_mansion.order_202401_new LIKE order_mansion.order_202401;
-- 第二步:慢慢搬(增量遷移)
INSERT INTO order_mansion.order_202401_new
SELECT * FROM single_apartment.orders
WHERE created_at >= '2024-01-01'
AND created_at < '2024-02-01';
-- 第三步:檢查有沒有落下的
SELECT
(SELECT COUNT(*) FROM single_apartment.orders
WHERE created_at >= '2024-01-01' AND created_at < '2024-02-01') as old_count,
(SELECT COUNT(*) FROM order_mansion.order_202401_new) as new_count;
-- 第四步:切換門牌號(重命名)
RENAME TABLE
order_mansion.order_202401 TO order_mansion.order_202401_backup,
order_mansion.order_202401_new TO order_mansion.order_202401;
第7步:全局ID生成——給每個數(shù)據(jù)發(fā)身份證
// 雪花算法:Twitter出品,必屬精品
public class SnowflakeIdGenerator {
private final long twepoch = 1288834974657L;
private final long workerIdBits = 5L;
private final long datacenterIdBits = 5L;
private final long sequenceBits = 12L;
private long workerId;
private long datacenterId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException("時間倒流了,檢查系統(tǒng)時間!");
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & 4095; // 2^12-1
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << 22) |
(datacenterId << 17) |
(workerId << 12) |
sequence;
}
// 全局唯一,趨勢遞增,適合分庫分表
}
三、注意事項——別墅區(qū)的物業(yè)管理條例
分布式事務(wù):樓上轉(zhuǎn)賬,樓下扣款,必須同時成功或失敗
// 使用Seata等分布式事務(wù)解決方案
@GlobalTransactional
public void transferMoney(long fromUser, long toUser, BigDecimal amount) {
// 1. 從用戶庫扣款
// 2. 向訂單庫插入記錄
// 要么都成功,要么都回滾
}
跨表查詢:想找張三的所有訂單?得問遍所有表!
-- 分表前:一步到位 SELECT * FROM orders WHERE user_id = 123; -- 分表后:變成偵查行動 SELECT * FROM order_202401 WHERE user_id = 123 UNION ALL SELECT * FROM order_202402 WHERE user_id = 123 UNION ALL -- ... 查遍所有月份表
JOIN操作:用戶表和訂單表在不同庫?只能業(yè)務(wù)層JOIN
// 1. 先從用戶庫查用戶 List<User> users = userDao.getUsers(condition); List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList()); // 2. 再去訂單庫查這些用戶的訂單 List<Order> orders = orderDao.getOrdersByUserIds(userIds); // 3. 在內(nèi)存中組裝 Map<Long, List<Order>> userOrdersMap = groupOrdersByUser(orders);
四、總結(jié)
分庫分表就像數(shù)據(jù)庫的成人禮,意味著你的業(yè)務(wù)從“小打小鬧”變成了“正經(jīng)事業(yè)”。但是:
不要過早優(yōu)化:如果你的數(shù)據(jù)還沒到百萬級別,別急著分表,就像不能因為將來可能變胖,現(xiàn)在就買XXL號褲子。
選擇合適的策略:按時間分?按地域分?按業(yè)務(wù)分?這就像選擇衣柜整理方式,有人喜歡按季節(jié),有人喜歡按顏色,適合的才是最好的。
準(zhǔn)備好工具:中間件(ShardingSphere、MyCat)是你的瑞士軍刀,監(jiān)控工具(Prometheus、Grafana)是你的健康檢測儀。
接受不完美:分庫分表后,事務(wù)復(fù)雜了,查詢麻煩了,運維困難了。但這就是成長的代價,就像長大后發(fā)現(xiàn)世界不是非黑即白。
最后提醒:分庫分表前,先試試這些“減肥方法”:
- 加索引(給東西貼標(biāo)簽)
- 優(yōu)化SQL(整理收納技巧)
- 升級硬件(換個大房子)
- 讀寫分離(男女分開用廁所)
只有當(dāng)這些都不夠用時,才考慮分庫分表這個大工程。記住,好的架構(gòu)是演進(jìn)而來的,不是設(shè)計出來的。就像好的婚姻,需要慢慢磨合,不能一開始就分房睡(分庫)還分床(分表)!
祝你的數(shù)據(jù)庫從“小公寓”順利升級到“豪華別墅區(qū)”,住得寬敞,查得飛快!
以上就是一文詳解MySQL實現(xiàn)分庫分表的詳細(xì)步驟的詳細(xì)內(nèi)容,更多關(guān)于MySQL分庫分表的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
mysql自增navicat_navicat如何設(shè)置主鍵自增
通過Navicat設(shè)置MySQL表的主鍵自增,步驟包括:打開Navicat連接數(shù)據(jù)庫,選擇表并設(shè)計,右擊id字段設(shè)置為主鍵,然后勾選自動遞增功能,這樣每次插入新記錄時,id字段都會自動遞增2025-01-01
美團(tuán)網(wǎng)技術(shù)團(tuán)隊分享的MySQL索引及慢查詢優(yōu)化教程
這篇文章主要介紹了美團(tuán)網(wǎng)技術(shù)團(tuán)隊分享的MySQL索引及慢查詢優(yōu)化教程,結(jié)合了實際的磁盤IO情況對一些優(yōu)化方案作出了分析,十分推薦!需要的朋友可以參考下2015-11-11

