成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

分布式鎖詳解:從數據庫實現到中間件選型

云計算 分布式
分布式鎖是分布式系統中的一個基礎組件,選擇合適的實現方案需要考慮:性能要求;可靠性要求;開發維護成本;團隊技術棧。

引言

在分布式系統中,我們經常需要對共享資源進行互斥訪問。比如:

  • 防止商品超賣
  • 避免重復下單
  • 確保任務只被處理一次
  • 保護共享資源不被并發修改

這就需要一個分布式鎖機制。與單機環境下的線程鎖不同,分布式鎖需要在多個服務實例間生效,這帶來了新的挑戰。

圖片圖片

分布式鎖的核心要求

一個可靠的分布式鎖必須滿足以下要求:

  1. 互斥性

在任意時刻,只能有一個客戶端持有鎖

不能出現多個客戶端同時持有鎖的情況

  1. 可重入性

同一個客戶端可以多次獲取同一把鎖

需要維護鎖的重入計數

  1. 防死鎖

客戶端崩潰時,鎖必須能自動釋放

鎖必須有過期機制

  1. 高可用

鎖服務不能成為系統瓶頸

鎖服務必須保證高可用

基于數據庫的實現

圖片圖片

1. 悲觀鎖實現

最簡單的方式是利用數據庫的行鎖:

-- 創建鎖表
CREATE TABLE distributed_lock (
    lock_key VARCHAR(50) PRIMARY KEY,
    lock_value VARCHAR(50),
    version INT,
    expire_time TIMESTAMP
);

-- 獲取鎖
SELECT * FROM distributed_lock 
WHERE lock_key = 'order_lock' 
FOR UPDATE;

Java 實現示例:

@Service
public class DatabaseDistributedLock {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Transactional
    public boolean acquireLock(String lockKey, String lockValue, long expireSeconds) {
        try {
            // 使用 FOR UPDATE 加鎖查詢
            String sql = "SELECT * FROM distributed_lock " +
                        "WHERE lock_key = ? FOR UPDATE";
            
            List<Map<String, Object>> result = jdbcTemplate.queryForList(
                sql, lockKey
            );
            
            if (result.isEmpty()) {
                // 鎖不存在,創建鎖
                jdbcTemplate.update(
                    "INSERT INTO distributed_lock " +
                    "(lock_key, lock_value, version, expire_time) " +
                    "VALUES (?, ?, 1, ?)",
                    lockKey,
                    lockValue,
                    LocalDateTime.now().plusSeconds(expireSeconds)
                );
                return true;
            }
            
            // 檢查鎖是否過期
            Map<String, Object> lock = result.get(0);
            LocalDateTime expireTime = ((Timestamp) lock.get("expire_time"))
                .toLocalDateTime();
                
            if (expireTime.isBefore(LocalDateTime.now())) {
                // 鎖已過期,更新鎖
                jdbcTemplate.update(
                    "UPDATE distributed_lock " +
                    "SET lock_value = ?, version = version + 1, expire_time = ? " +
                    "WHERE lock_key = ?",
                    lockValue,
                    LocalDateTime.now().plusSeconds(expireSeconds),
                    lockKey
                );
                return true;
            }
            
            return false;
        } catch (Exception e) {
            return false;
        }
    }
}

2. 樂觀鎖實現

使用版本號實現樂觀鎖:

@Service
public class OptimisticLock {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public boolean acquireLock(String lockKey, String lockValue, int version) {
        int updated = jdbcTemplate.update(
            "UPDATE distributed_lock " +
            "SET lock_value = ?, version = version + 1 " +
            "WHERE lock_key = ? AND version = ?",
            lockValue,
            lockKey,
            version
        );
        
        return updated > 0;
    }
}

數據庫實現的優缺點:

  • 優點:

實現簡單

容易理解

不需要額外組件

  • 缺點:

性能較差

數據庫壓力大

無法優雅處理鎖超時

基于 Redis 的實現

1. 單節點實現

使用 Redis 的 SETNX 命令:

@Service
public class RedisDistributedLock {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    public boolean acquireLock(String lockKey, String lockValue, long expireSeconds) {
        return redisTemplate.opsForValue()
            .setIfAbsent(lockKey, lockValue, expireSeconds, TimeUnit.SECONDS);
    }
    
    public boolean releaseLock(String lockKey, String lockValue) {
        // 使用 Lua 腳本確保原子性
        String script = 
            "if redis.call('get', KEYS[1]) == ARGV[1] then " +
            "return redis.call('del', KEYS[1]) " +
            "else " +
            "return 0 " +
            "end";
            
        return redisTemplate.execute(
            new DefaultRedisScript<>(script, Boolean.class),
            Collections.singletonList(lockKey),
            lockValue
        );
    }
}

2. RedLock 算法

在 Redis 集群環境下,使用 RedLock 算法:

public class RedLock {
    
    private final List<StringRedisTemplate> redisList;
    private final int quorum;  // 大多數節點數
    
    public boolean acquireLock(String lockKey, String lockValue, long expireMillis) {
        int acquiredLocks = 0;
        long startTime = System.currentTimeMillis();
        
        // 嘗試在每個節點上獲取鎖
        for (StringRedisTemplate redis : redisList) {
            if (tryAcquireLock(redis, lockKey, lockValue, expireMillis)) {
                acquiredLocks++;
            }
        }
        
        // 計算獲取鎖消耗的時間
        long elapsedTime = System.currentTimeMillis() - startTime;
        long remainingTime = expireMillis - elapsedTime;
        
        // 判斷是否獲取到足夠的鎖
        if (acquiredLocks >= quorum && remainingTime > 0) {
            return true;
        } else {
            // 釋放所有獲取的鎖
            releaseLocks(lockKey, lockValue);
            return false;
        }
    }
    
    private boolean tryAcquireLock(
        StringRedisTemplate redis, 
        String lockKey, 
        String lockValue, 
        long expireMillis
    ) {
        try {
            return redis.opsForValue()
                .setIfAbsent(lockKey, lockValue, expireMillis, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            return false;
        }
    }
}

Redis 實現的優缺點:

  • 優點:

性能高

實現相對簡單

支持自動過期

  • 缺點:

需要額外維護 Redis 集群

RedLock 算法實現復雜

時鐘依賴問題

基于 ZooKeeper 的實現

圖片圖片

利用 ZooKeeper 的臨時節點機制:

public class ZookeeperDistributedLock {
    
    private final CuratorFramework client;
    private final String lockPath;
    
    public boolean acquireLock(String lockKey) throws Exception {
        // 創建臨時節點
        String path = lockPath + "/" + lockKey;
        try {
            client.create()
                .creatingParentsIfNeeded()
                .withMode(CreateMode.EPHEMERAL)
                .forPath(path);
            return true;
        } catch (NodeExistsException e) {
            return false;
        }
    }
    
    public void releaseLock(String lockKey) throws Exception {
        String path = lockPath + "/" + lockKey;
        client.delete().forPath(path);
    }
    
    // 實現可重入鎖
    public class ReentrantZookeeperLock {
        private final ThreadLocal<Integer> lockCount = new ThreadLocal<>();
        
        public boolean acquire() throws Exception {
            Integer count = lockCount.get();
            if (count != null && count > 0) {
                // 入
                lockCount.set(count + 1);
                return true;
            }
            
            if (acquireLock("lock")) {
                lockCount.set(1);
                return true;
            }
            return false;
        }
        
        public void release() throws Exception {
            Integer count = lockCount.get();
            if (count == null) {
                return;
            }
            
            count--;
            if (count > 0) {
                lockCount.set(count);
            } else {
                lockCount.remove();
                releaseLock("lock");
            }
        }
    }
}

ZooKeeper 實現的優缺點:

  • 優點:

可靠性高

自動釋放鎖

支持監聽機制

  • 缺點:

性能一般

實現復雜

需要維護 ZooKeeper 集群

業務場景分析

1. 秒殺場景

場景特點:

  • 并發量極高
  • 時間窗口集中
  • 對性能要求極高
  • 數據一致性要求高

推薦方案: Redis + Lua腳本

@Service
public class SeckillLockService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    public boolean trySecKill(String productId, String userId) {
        // Lua腳本保證原子性
        String script = 
            "if redis.call('exists', KEYS[1]) == 0 then " +
            "  redis.call('set', KEYS[1], ARGV[1]) " +
            "  redis.call('decrby', KEYS[2], 1) " +
            "  return 1 " +
            "end " +
            "return 0";
            
        List<String> keys = Arrays.asList(
            "seckill:lock:" + productId + ":" + userId,
            "seckill:stock:" + productId
        );
        
        return redisTemplate.execute(
            new DefaultRedisScript<>(script, Boolean.class),
            keys,
            "1"
        );
    }
}

原因分析:

  • Redis 的高性能滿足并發要求
  • Lua 腳本保證原子性
  • 內存操作速度快
  • 集群方案保證可用性

2. 定時任務場景

場景特點:

  • 多實例部署
  • 任務不能重復執行
  • 故障轉移需求
  • 實時性要求不高

推薦方案: ZooKeeper

public class ScheduledTaskLock {
    
    private final CuratorFramework client;
    
    public void executeTask() {
        String taskPath = "/scheduled-tasks/daily-report";
        try {
            // 創建臨時節點
            client.create()
                .creatingParentsIfNeeded()
                .withMode(CreateMode.EPHEMERAL)
                .forPath(taskPath);
                
            try {
                // 執行任務
                generateDailyReport();
            } finally {
                // 刪除節點
                client.delete().forPath(taskPath);
            }
        } catch (NodeExistsException e) {
            // 其他實例正在執行
            log.info("Task is running on other instance");
        }
    }
}

原因分析:

  • ZooKeeper 的臨時節點特性保證故障時自動釋放鎖
  • 強一致性保證任務不會重復執行
  • Watch 機制便于監控任務執行狀態

3. 訂單支付場景

場景特點:

  • 并發量適中
  • 數據一致性要求高
  • 需要事務支持
  • 有業務回滾需求

推薦方案: 數據庫行鎖 + 事務

@Service
public class PaymentLockService {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Transactional
    public boolean processPayment(String orderId, BigDecimal amount) {
        // 使用 FOR UPDATE 鎖定訂單記錄
        String sql = "SELECT * FROM orders WHERE order_id = ? FOR UPDATE";
        Map<String, Object> order = jdbcTemplate.queryForMap(sql, orderId);
        
        // 檢查訂單狀態
        if (!"PENDING".equals(order.get("status"))) {
            return false;
        }
        
        // 執行支付邏輯
        jdbcTemplate.update(
            "UPDATE orders SET status = 'PAID' WHERE order_id = ?",
            orderId
        );
        
        // 記錄支付流水
        jdbcTemplate.update(
            "INSERT INTO payment_log (order_id, amount) VALUES (?, ?)",
            orderId, amount
        );
        
        return true;
    }
}

原因分析:

  • 數據庫事務保證數據一致性
  • 行鎖防止并發支付
  • 便于與其他業務集成
  • 支持事務回滾

4. 庫存扣減場景

場景特點:

  • 并發量較高
  • 需要預占庫存
  • 需要處理超時釋放
  • 對性能要求較高

推薦方案: Redis + 延時隊列

@Service
public class InventoryLockService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    public boolean lockInventory(String productId, int quantity, String orderId) {
        // 加鎖并預占庫存
        String script = 
            "local stock = redis.call('get', KEYS[1]) " +
            "if stock and tonumber(stock) >= tonumber(ARGV[1]) then " +
            "  redis.call('decrby', KEYS[1], ARGV[1]) " +
            "  redis.call('setex', KEYS[2], 1800, ARGV[1]) " +
            "  return 1 " +
            "end " +
            "return 0";
            
        List<String> keys = Arrays.asList(
            "inventory:" + productId,
            "inventory:lock:" + orderId
        );
        
        boolean locked = redisTemplate.execute(
            new DefaultRedisScript<>(script, Boolean.class),
            keys,
            String.valueOf(quantity)
        );
        
        if (locked) {
            // 添加延時釋放任務
            redisTemplate.opsForZSet().add(
                "inventory:timeout",
                orderId,
                System.currentTimeMillis() + 1800000
            );
        }
        
        return locked;
    }
}

原因分析:

  • Redis 的高性能滿足并發要求
  • 延時隊列處理超時釋放
  • 原子操作保證數據一致性
  • 便于擴展和監控

實現方案對比

特性

數據庫

Redis

ZooKeeper

性能

可靠性

實現復雜度

維護成本

自動釋放

需要額外實現

支持

支持

可重入性

需要額外實現

需要額外實現

需要額外實現

最佳實踐

  1. 選擇建議

簡單場景:使用數據庫實現

高性能要求:使用 Redis 實現

高可靠要求:使用 ZooKeeper 實現

  1. 實現建議

設置合理的超時時間

實現可重入機制

添加監控和告警

做好日志記錄

  1. 使用建議

縮小鎖的粒度

減少鎖的持有時間

避免死鎖

做好異常處理

結論

分布式鎖是分布式系統中的一個基礎組件,選擇合適的實現方案需要考慮:

  • 性能要求
  • 可靠性要求
  • 開發維護成本
  • 團隊技術棧

沒有最好的方案,只有最合適的方案。在實際應用中,要根據具體場景選擇合適的實現方式。

正文內容從這里開始(可直接省略,亦可配圖說明)。

責任編輯:武曉燕 來源: 架構成長指南
相關推薦

2021-11-14 16:07:35

中間件阿里Seata

2021-11-26 06:43:19

Java分布式

2019-08-12 11:00:59

美團網MySQL數據庫

2012-11-30 10:21:46

移動中間件

2025-03-27 11:03:18

2019-08-19 10:24:33

分布式事務數據庫

2017-12-01 05:04:32

數據庫中間件Atlas

2017-11-27 05:36:16

數據庫中間件TDDL

2017-11-27 05:06:42

數據庫中間件cobar

2024-05-07 07:58:10

數據架構大數據中間件架構

2018-02-24 19:37:33

Java8數據庫中間件

2015-06-16 10:39:43

NoSQL分布式算法

2022-08-01 18:33:45

關系型數據庫大數據

2024-12-06 08:29:29

2011-08-10 13:03:58

CJDBC數據庫集群

2017-05-23 18:55:05

mysql-proxy數據庫架構

2022-07-21 07:31:41

數據庫分布式

2017-12-04 09:00:00

金融開源軟件分布式消息中間件

2017-07-26 09:41:28

MyCATSQLMongoDB

2024-05-06 00:00:00

.NET分布式鎖技術
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品久久久久久久久久 | 成人免费毛片在线观看 | 在线免费观看成人 | 一本在线 | 日韩视频在线一区二区 | www.久久久久久久久久久 | 日本超碰 | 成人在线小视频 | 成人a免费 | 中文字幕免费在线 | 日韩一区二区三区在线 | 91高清视频 | 天久久| 久久91| 国产精品精品久久久 | 中文字幕av免费 | 精品婷婷 | 美女国产精品 | 在线永久看片免费的视频 | 久久久久国产一区二区三区不卡 | 久久久91| 日本不卡免费新一二三区 | 中文字幕av第一页 | 在线只有精品 | 国产精品免费一区二区三区四区 | av黄色免费在线观看 | 伊人电影院av | 91在线精品秘密一区二区 | 午夜免费在线 | 一区二区三区精品视频 | 蜜月va乱码一区二区三区 | 亚洲精品乱码久久久久久9色 | 天天操天天舔 | 人人看人人射 | 日韩欧美在线视频 | 亚洲精品成人av久久 | 国产综合在线视频 | 中文字幕精品一区 | 国产日韩欧美91 | 丁香五月网久久综合 | www成人免费视频 |