精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

千萬別這么用 Redis 和 @Transactional!我踩過的坑能繞地球三圈

開發 前端
故事得從三年前說起,那時候咱剛接手一個電商項目,負責用戶積分系統。需求是用戶下單成功后,扣減賬戶積分,同時記錄積分變更日志。咱心想,這事兒簡單啊,Redis 存用戶實時積分,數據庫存積分變更記錄,再用 @Transactional 保證數據庫操作的原子性,完美!

兄弟們,咱今天來嘮嘮 Redis 和 @Transactional 這倆哥們兒,要是搭伙兒沒搭好,能讓你在代碼里踩坑到懷疑人生,我當年踩過的坑連起來都能繞地球三圈了。咱先說好,今兒這文章不賣關子,直接上硬貨,從坑怎么來的,到怎么填坑,全給你整明白咯。

一、那些年,我在 Redis 和事務里踩的第一個大坑

故事得從三年前說起,那時候咱剛接手一個電商項目,負責用戶積分系統。需求是用戶下單成功后,扣減賬戶積分,同時記錄積分變更日志。咱心想,這事兒簡單啊,Redis 存用戶實時積分,數據庫存積分變更記錄,再用 @Transactional 保證數據庫操作的原子性,完美!

于是,咱寫了這么段代碼:

@Transactional(rollbackFor = Exception.class)
public void deduct積分(Long userId, Integer deductScore) {
    // 先扣 Redis 里的積分
    redisTemplate.opsForValue().increment(userId.toString(), -deductScore);
    // 再往數據庫里插積分變更記錄
    IntegralLog integralLog = new IntegralLog();
    integralLog.setUserId(userId);
    integralLog.setDeductScore(deductScore);
    integralLog.setCreateTime(new Date());
    integralLogMapper.insert(integralLog);
    // 模擬一個可能出現的異常
    if (deductScore > 1000) {
        throw new RuntimeException("積分扣減超過限制");
    }
}

代碼寫完,自測的時候沒問題,下單扣積分,記錄日志,一切正常。可上線沒兩天,運營小姐姐就來找咱了,說有用戶反饋積分扣了,但訂單沒成功,而且積分也沒恢復。咱趕緊查日志,發現確實有數據庫操作拋異常回滾了,但 Redis 里的積分沒回來。

這咋回事呢?咱一拍腦袋,恍然大悟:Redis 的操作根本不在數據庫事務里啊!@Transactional 管的是數據庫的事兒,對 Redis 那是鞭長莫及。咱先操作了 Redis,再操作數據庫,要是數據庫操作失敗回滾了,Redis 里的數據可不會跟著回滾,這不就出現數據不一致了嘛。就好比你先把兜里的錢給別人了,然后發現別人沒給你貨,想把錢要回來,可人家已經把錢揣兜里跑了,你說糟心不糟心。

二、以為把 Redis 操作放事務里就萬事大吉?Too Young Too Simple!

吃一塹長一智,咱知道不能先操作 Redis 了,那咱把 Redis 的操作放到數據庫事務里面總行了吧?咱又改了改代碼,這次先操作數據庫,再操作 Redis,而且都放在 @Transactional 注解的方法里:

@Transactional(rollbackFor = Exception.class)
public void deduct積分(Long userId, Integer deductScore) {
    // 先往數據庫里插積分變更記錄
    IntegralLog integralLog = new IntegralLog();
    integralLog.setUserId(userId);
    integralLog.setDeductScore(deductScore);
    integralLog.setCreateTime(new Date());
    integralLogMapper.insert(integralLog);
    // 再扣 Redis 里的積分
    redisTemplate.opsForValue().increment(userId.toString(), -deductScore);
    // 模擬一個可能出現的異常
    if (deductScore > 1000) {
        throw new RuntimeException("積分扣減超過限制");
    }
}

這次咱想,數據庫操作和 Redis 操作都在事務里,要是拋異常了,數據庫回滾,Redis 也能回滾吧?結果一測試,傻眼了:數據庫操作回滾了,可 Redis 里的積分還是被扣了。這是為啥呢?咱去查了查 Redis 的文檔和 Spring 事務的原理,才知道 Redis 的操作本身不是事務性的,Spring 的 @Transactional 只能管理數據庫事務,對于 Redis 這種外部資源的操作,它管不了。也就是說,雖然 Redis 操作寫在了事務方法里,但它不會隨著數據庫事務的回滾而回滾。就好比你帶著一個不聽話的小弟去辦事,你說咱們得一起行動,要是出事兒了就一起撤,結果這小弟自己跑了,根本不管你。

那有人可能會問了,能不能讓 Redis 支持事務,然后和數據庫事務一起提交或回滾呢?理論上是可以的,但實際操作起來可麻煩了。Redis 本身的事務和數據庫的事務不一樣,它的事務只是把多個命令打包執行,不支持回滾(除非在命令入隊時出錯),而且和數據庫事務的協調需要復雜的分布式事務解決方案,比如兩階段提交(2PC),這會增加系統的復雜度和性能開銷,一般不太建議這么做。

三、當 Redis 遇見可重復讀事務,又一個坑在等著你

咱再來說說另一個場景,在一個事務里多次讀取 Redis 的數據,而且數據庫事務的隔離級別是可重復讀。比如咱要根據用戶的積分來判斷是否能參加某個活動,在事務里先讀取 Redis 里的積分,然后進行一些業務處理,最后再讀取一次積分,看看有沒有變化。

@Transactional(isolation = Isolation.REPEATABLE_READ, rollbackFor = Exception.class)
public void checkAndDeduct積分(Long userId, Integer deductScore) {
    // 第一次讀取 Redis 積分
    Integer currentScore = Integer.parseInt(redisTemplate.opsForValue().get(userId.toString()));
    // 業務處理,比如判斷積分是否足夠
    if (currentScore < deductScore) {
        throw new RuntimeException("積分不足");
    }
    // 模擬耗時的業務處理
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    // 第二次讀取 Redis 積分
    Integer updatedScore = Integer.parseInt(redisTemplate.opsForValue().get(userId.toString()));
    // 發現積分可能已經被其他線程修改了
    if (updatedScore < deductScore) {
        throw new RuntimeException("積分不足");
    }
    // 扣減數據庫積分記錄
    IntegralLog integralLog = new IntegralLog();
    integralLog.setUserId(userId);
    integralLog.setDeductScore(deductScore);
    integralLog.setCreateTime(new Date());
    integralLogMapper.insert(integralLog);
    // 扣減 Redis 積分
    redisTemplate.opsForValue().increment(userId.toString(), -deductScore);
}

在數據庫的可重復讀隔離級別下,事務內多次讀取數據庫數據是一致的,因為數據庫通過 MVCC(多版本并發控制)實現了可重復讀。但 Redis 是內存數據庫,沒有 MVCC 機制,它的數據是實時更新的。所以在事務執行過程中,其他線程可能會修改 Redis 里的數據,導致同一事務內多次讀取 Redis 的數據不一致,這就會出現業務判斷錯誤的情況。

比如在上面的例子中,第一次讀取積分時足夠扣減,但是在業務處理的這一秒鐘內,其他線程可能已經扣減了該用戶的積分,導致第二次讀取時積分不足,這時候再進行扣減就會出錯。而數據庫的可重復讀并不能保證 Redis 數據的一致性,這就是一個典型的分布式數據不一致問題。

四、這些 Redis 數據結構和事務搭配,分分鐘讓你翻車

(一)List 結構的左進右出操作

咱在使用 Redis 的 List 結構做隊列的時候,經常會用到 lpush 和 rpop 操作,模擬一個先進先出的隊列。比如在訂單處理系統中,把訂單號按順序存入 List,然后消費者從另一端取出訂單號進行處理。

@Transactional(rollbackFor = Exception.class)
public void processOrder() {
    // 從 Redis 隊列右彈出一個訂單號
    String orderId = redisTemplate.opsForList().rightPop("orderQueue");
    if (orderId != null) {
        // 根據訂單號處理訂單,比如更新訂單狀態到數據庫
        Order order = orderMapper.selectByPrimaryKey(orderId);
        order.setStatus("處理中");
        orderMapper.updateByPrimaryKey(order);
        // 模擬處理過程中出現異常
        if (order.getAmount() > 10000) {
            throw new RuntimeException("大額訂單處理異常");
        }
        // 處理完成,記錄處理日志
        OrderProcessLog log = new OrderProcessLog();
        log.setOrderId(orderId);
        log.setProcessTime(new Date());
        orderProcessLogMapper.insert(log);
    }
}

看起來沒問題吧?但是如果在處理訂單的過程中,數據庫操作拋異常回滾了,但是 Redis 里的訂單號已經被 rpop 出去了,這個訂單就相當于丟失了,不會再被其他消費者處理。這就是因為 Redis 的 rpop 操作和數據庫事務沒有關聯,一旦執行就無法回滾,導致數據不一致。

(二)Set 結構的交集操作

還有一次,咱需要根據用戶的標簽來推薦商品,使用 Redis 的 Set 結構存儲用戶標簽和商品標簽,然后通過交集操作找出符合用戶標簽的商品。

@Transactional(rollbackFor = Exception.class)
public List<String> recommendGoods(String userId) {
    // 獲取用戶的標簽集合
    Set<String> userTags = redisTemplate.opsForSet().members("user:tags:" + userId);
    if (userTags == null || userTags.isEmpty()) {
        return new ArrayList<>();
    }
    // 獲取所有商品的標簽集合,并計算交集
    Set<String> allGoods = redisTemplate.opsForSet().members("all:goods");
    Set<String> recommendedGoods = new HashSet<>();
    for (String good : allGoods) {
        Set<String> goodTags = redisTemplate.opsForSet().members("good:tags:" + good);
        if (goodTags != null && !goodTags.isEmpty() && goodTags.containsAll(userTags)) {
            recommendedGoods.add(good);
        }
    }
    // 將推薦的商品存入數據庫推薦表
    for (String good : recommendedGoods) {
        RecommendRecord record = new RecommendRecord();
        record.setUserId(userId);
        record.setGoodId(good);
        record.setRecommendTime(new Date());
        recommendRecordMapper.insert(record);
    }
    // 模擬異常
    if (recommendedGoods.size() > 100) {
        throw new RuntimeException("推薦商品數量過多");
    }
    return new ArrayList<>(recommendedGoods);
}

這里的問題在于,在計算交集的過程中,Redis 的 Set 成員可能會被其他線程修改,導致推薦結果不準確。而且,如果數據庫操作回滾了,已經計算出來的推薦結果并不會影響 Redis 里的數據,但是推薦記錄沒有存入數據庫,這就會出現推薦結果和數據庫記錄不一致的情況。

(三)SortedSet 結構的分數更新

在積分排名系統中,咱常用 SortedSet 來存儲用戶的積分和排名。當用戶積分變化時,需要更新 SortedSet 中的分數。

@Transactional(rollbackFor = Exception.class)
public void updateScore(Long userId, Integer score) {
    // 更新 Redis 里的積分和排名
    redisTemplate.opsForZSet().add("user:score:rank", userId.toString(), score);
    // 更新數據庫里的用戶積分
    User user = userMapper.selectByPrimaryKey(userId);
    user.setScore(score);
    userMapper.updateByPrimaryKey(user);
    // 模擬異常
    if (score < 0) {
        throw new RuntimeException("積分不能為負數");
    }
}

同樣的問題,要是數據庫更新失敗回滾了,Redis 里的積分和排名已經更新了,這就導致數據庫和 Redis 數據不一致。而且 SortedSet 的排名是根據分數實時計算的,一旦分數錯誤,排名也會跟著錯,影響整個排名系統的準確性。

五、填坑指南:正確使用 Redis 和 @Transactional 的姿勢

(一)先數據庫后 Redis,事務保護數據庫

經過前面的坑,咱總結出一個基本原則:在涉及數據庫和 Redis 操作的事務中,優先操作數據庫,再操作 Redis,并且利用數據庫事務的原子性來保證業務的一致性。如果數據庫操作失敗,回滾事務,Redis 操作就不會執行(因為 Redis 操作在數據庫操作之后)。

比如前面的扣積分場景,正確的做法是:

public void deduct積分(Long userId, Integer deductScore) {
    // 這里不使用 @Transactional 注解,而是在數據庫操作的服務層方法使用
    try {
        // 先執行數據庫操作,利用數據庫事務
        doDeductInDatabase(userId, deductScore);
        // 數據庫操作成功后,再操作 Redis
        redisTemplate.opsForValue().increment(userId.toString(), -deductScore);
    } catch (Exception e) {
        // 如果出現異常,需要根據情況處理 Redis 數據,比如恢復積分
        // 這里需要注意,Redis 的回滾需要手動處理,因為它沒有事務
        redisTemplate.opsForValue().increment(userId.toString(), deductScore);
        throw new RuntimeException("積分扣減失敗", e);
    }
}
@Transactional(rollbackFor = Exception.class)
private void doDeductInDatabase(Long userId, Integer deductScore) {
    // 檢查數據庫中的積分是否足夠
    UserIntegral userIntegral = userIntegralMapper.selectByUserId(userId);
    if (userIntegral.getScore() < deductScore) {
        throw new RuntimeException("積分不足");
    }
    // 扣減數據庫積分
    userIntegral.setScore(userIntegral.getScore() - deductScore);
    userIntegralMapper.updateByUserId(userIntegral);
    // 插入積分變更記錄
    IntegralLog integralLog = new IntegralLog();
    integralLog.setUserId(userId);
    integralLog.setDeductScore(deductScore);
    integralLog.setCreateTime(new Date());
    integralLogMapper.insert(integralLog);
}

這樣做的好處是,數據庫操作在事務中,保證了原子性,只有數據庫操作成功了,才會去操作 Redis。如果數據庫操作失敗,事務回滾,Redis 也不會有錯誤的數據。不過要注意,如果 Redis 操作拋出異常,需要手動處理數據庫事務,比如可以使用 @Transactional 的異常處理機制,或者在 Redis 操作失敗時回滾數據庫事務。

(二)異步處理 Redis,解耦事務依賴

如果對實時性要求不是特別高,可以把 Redis 的操作放到異步線程或者消息隊列中處理,這樣就不會和數據庫事務耦合在一起了。比如使用 Spring 的 @Async 注解,或者引入 RabbitMQ、Kafka 等消息中間件。

以 @Async 為例:

@Transactional(rollbackFor = Exception.class)
public void deduct積分(Long userId, Integer deductScore) {
    // 執行數據庫操作
    UserIntegral userIntegral = userIntegralMapper.selectByUserId(userId);
    if (userIntegral.getScore() < deductScore) {
        throw new RuntimeException("積分不足");
    }
    userIntegral.setScore(userIntegral.getScore() - deductScore);
    userIntegralMapper.updateByUserId(userIntegral);
    IntegralLog integralLog = new IntegralLog();
    integralLog.setUserId(userId);
    integralLog.setDeductScore(deductScore);
    integralLog.setCreateTime(new Date());
    integralLogMapper.insert(integralLog);
    // 異步處理 Redis 操作
    asyncService.updateRedisScore(userId, deductScore);
}
// 異步服務類
@Service
public class AsyncService {
    @Async
    public void updateRedisScore(Long userId, Integer deductScore) {
        try {
            redisTemplate.opsForValue().increment(userId.toString(), -deductScore);
        } catch (Exception e) {
            // 記錄異常日志,后續可以通過補償機制處理
            log.error("更新 Redis 積分失敗,userId: {}, deductScore: {}", userId, deductScore, e);
        }
    }
}

這樣數據庫事務和 Redis 操作解耦了,數據庫事務成功提交后,異步執行 Redis 操作。即使 Redis 操作失敗,也可以通過日志記錄,后續通過定時任務或者補償接口來修復數據,提高了系統的可用性和容錯性。

(三)利用 Redis 管道和事務,減少網絡開銷

雖然 Redis 本身的事務不能和數據庫事務協同工作,但在批量操作 Redis 時,可以使用 Redis 的管道(Pipeline)和事務來減少網絡開銷,保證一批 Redis 命令的原子性(在命令入隊階段不出錯的情況下)。

比如批量插入數據到 Redis 時:

public void batchUpdateRedis(List<Long> userIds, List<Integer> deductScores) {
    redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
        for (int i = 0; i < userIds.size(); i++) {
            String userId = userIds.get(i).toString();
            Integer deductScore = deductScores.get(i);
            connection.incrBy(userId.getBytes(), -deductScore);
        }
        return null;
    });
}

管道可以將多個 Redis 命令打包發送,減少客戶端和服務器之間的網絡往返次數,提高性能。而 Redis 自身的事務可以保證這一批命令要么全部執行,要么都不執行(如果在入隊階段有錯誤),雖然和數據庫事務不同,但在純 Redis 操作的場景下,能保證 Redis 數據的一致性。

(四)緩存數據版本號,解決可重復讀問題

針對在事務中多次讀取 Redis 數據不一致的問題,可以給緩存的數據添加版本號,每次讀取數據時同時讀取版本號,在更新數據時檢查版本號是否一致,保證數據的一致性。

比如:

@Transactional(isolation = Isolation.REPEATABLE_READ, rollbackFor = Exception.class)
publicvoid checkAndDeduct積分(Long userId, Integer deductScore) {
    // 第一次讀取 Redis 積分和版本號
    String scoreKey = "user:score:" + userId;
    String versionKey = "user:score:version:" + userId;
    Integer currentScore = Integer.parseInt(redisTemplate.opsForValue().get(scoreKey));
    Long version = Long.parseLong(redisTemplate.opsForValue().get(versionKey));
    // 業務處理
    if (currentScore < deductScore) {
        thrownew RuntimeException("積分不足");
    }
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    // 第二次讀取版本號,檢查是否有變化
    Long newVersion = Long.parseLong(redisTemplate.opsForValue().get(versionKey));
    if (!version.equals(newVersion)) {
        thrownew RuntimeException("積分數據已被修改,操作失敗");
    }
    // 扣減數據庫積分記錄
    IntegralLog integralLog = new IntegralLog();
    integralLog.setUserId(userId);
    integralLog.setDeductScore(deductScore);
    integralLog.setCreateTime(new Date());
    integralLogMapper.insert(integralLog);
    // 扣減 Redis 積分,并更新版本號
    redisTemplate.opsForValue().increment(scoreKey, -deductScore);
    redisTemplate.opsForValue().increment(versionKey);
}

通過版本號的方式,在事務中檢查數據是否被修改過,如果被修改過,就拋出異常,終止操作,保證了業務邏輯的正確性。

(五)使用分布式事務框架,解決跨資源事務問題

如果項目中涉及到多個數據庫和 Redis 等多個資源的事務協調,就需要使用分布式事務框架了,比如 Seata、TCC-Transaction 等。這些框架可以幫助我們管理跨資源的事務,保證最終一致性。

以 Seata 的 AT 模式為例,大致步驟如下:

  1. 定義事務的入口,開啟全局事務。
  2. 在操作數據庫和 Redis 的服務中,注冊分支事務。
  3. 數據庫操作通過 Seata 的代理數據源來實現自動生成回滾日志,保證可回滾性。
  4. Redis 操作需要手動實現補償邏輯,比如在分支事務回滾時,執行相反的操作(如增加積分來補償之前的扣減)。

不過分布式事務框架比較復雜,會增加系統的復雜度和性能開銷,所以在使用時需要根據項目的實際情況來選擇,不要盲目引入。

六、總結:踩坑不可怕,怕的是不總結

咱今天嘮了這么多 Redis 和 @Transactional 一起使用時的坑,總結起來就是一句話:Redis 操作和數據庫事務是兩個不同的世界,不能想當然地認為它們會自動協同工作。在使用時,一定要明確它們的邊界,根據業務場景選擇合適的方案。

記住這幾個關鍵點:

  1. 優先保證數據庫事務的原子性,Redis 操作放在數據庫操作之后,且做好異常處理和補償機制。
  2. 對實時性要求不高的場景,異步處理 Redis 操作,解耦事務依賴。
  3. 復雜的分布式事務場景,使用專業的分布式事務框架,不要自己硬剛。
  4. 了解 Redis 數據結構的特性,避免在事務中使用可能導致數據不一致的操作。

咱踩過的坑,希望你們別再踩了。要是覺得這篇文章有用,趕緊收藏轉發,讓更多的兄弟避避坑。

責任編輯:武曉燕 來源: 石杉的架構筆記
相關推薦

2023-08-03 07:13:59

2024-11-07 10:04:48

2021-09-24 13:45:00

CTO說直播

2024-05-06 00:00:00

緩存高并發數據

2014-09-10 10:14:14

2009-07-03 16:21:58

IT系統數據中心運維管理

2012-02-21 09:22:45

2019-06-26 08:30:32

計算機互聯網iOS

2025-03-28 08:40:00

C#異步編程

2024-04-01 08:05:27

Go開發Java

2013-06-27 14:27:10

華為數字軌道華為

2011-02-22 09:24:30

諾基亞微軟

2021-03-17 10:25:45

數字化轉型首席信息官IT文化變革

2020-12-07 07:48:35

K8sDockerKubernetes

2017-04-20 13:32:21

人工智能深度學習AI

2019-10-30 14:44:41

Prometheus開源監控系統

2017-07-17 15:46:20

Oracle并行機制

2021-08-06 09:20:41

IT管理IT領導者CIO

2025-06-03 06:30:05

2025-10-16 08:10:59

點贊
收藏

51CTO技術棧公眾號

成人开心激情| 毛片免费在线观看| 亚洲日本欧美| 亚洲色图18p| 中国黄色片一级| free性欧美16hd| 欧美—级在线免费片| 3d蒂法精品啪啪一区二区免费| 久久中文字幕无码| 国产欧美日韩在线观看视频| 777久久久精品| 国产一区 在线播放| 韩国免费在线视频| 国产成人啪免费观看软件| 欧美自拍视频在线| 中国毛片直接看| 蜜臀91精品国产高清在线观看| 欧美日高清视频| 国产乱子伦农村叉叉叉| 免费人成在线观看播放视频| 国产精品正在播放| 国产精品视频免费观看www| 四虎永久免费在线| 久久91成人| 精品国产91乱码一区二区三区| youjizzxxxx18| www.youjizz.com在线| 亚洲色图欧洲色图婷婷| 国产一区精品视频| 国产xxxx孕妇| 日本va欧美va瓶| 91tv亚洲精品香蕉国产一区7ujn| www.av成人| 欧美艳星介绍134位艳星| 亚洲成人精品久久| 久久久国产精品久久久| 伊人久久大香伊蕉在人线观看热v 伊人久久大香线蕉综合影院首页 伊人久久大香 | 男人天堂新网址| 视频一区二区三区不卡| 91视频com| 精品免费国产| 日韩一级片免费观看| 国产精品小仙女| 成人xxxxx| 一区二区日韩在线观看| 蜜臀久久99精品久久久画质超高清| 欧美中文字幕视频在线观看| 日本学生初尝黑人巨免费视频| 911精品美国片911久久久| 自拍亚洲一区欧美另类| 国产高清一区二区三区四区| 夜夜春成人影院| 亚洲美女av在线| 激情综合丁香五月| 色老板在线视频一区二区| 精品久久久久久久一区二区蜜臀| 国产欧美精品一二三| 亚洲精品伊人| 欧美精品一级二级| 国产三级精品三级在线| 精品国产一区二| 欧美一级日韩免费不卡| 日韩欧美理论片| 久久久久久亚洲精品美女| 欧美一级艳片视频免费观看| 成人三级做爰av| 成人高潮a毛片免费观看网站| 欧美一区二区三区在线看| 国产人妻精品久久久久野外| 欧美三级一区| 亚洲激情小视频| 色欲av无码一区二区三区| 国产综合久久久| 日韩一二三在线视频播| 69xx绿帽三人行| 精品动漫一区| 人人澡人人澡人人看欧美| 懂色av中文字幕| 精东粉嫩av免费一区二区三区| 91亚洲va在线va天堂va国| 精品人妻一区二区三区蜜桃| av影院午夜一区| 日韩成人av电影在线| 色三级在线观看| 亚洲二区在线观看| 日本三级免费观看| 亚洲精品tv| 亚洲国产天堂久久综合| 蜜臀久久99精品久久久久久| 先锋资源久久| 91成人福利在线| 91成人在线免费| 成人动漫视频在线| 色一情一乱一伦一区二区三区| 成人影欧美片| 欧美日韩国产一区中文午夜| 黄色永久免费网站| 高清日韩欧美| 神马久久桃色视频| 日本三级片在线观看| 人人狠狠综合久久亚洲| 成人看片在线| 成人免费一区二区三区视频网站| 亚洲精品欧美综合四区| 欧美日韩在线一| 日韩福利影视| 亚洲人精品午夜在线观看| 免费国产羞羞网站美图| 久久免费黄色| 国产精品免费区二区三区观看| 黄色小视频在线免费观看| 一区二区三区日韩在线观看| 国产裸体舞一区二区三区 | 99久久精品免费看国产四区| 日色在线视频| 亚洲永久精品国产| 亚洲综合欧美激情| 亚洲va久久| 久久久久久久久久久av| 国产精品久久久久久69| www.欧美日韩国产在线| 法国空姐在线观看免费| 成人精品电影在线| 亚洲国产成人在线视频| 国产精品免费人成网站酒店| 日韩国产欧美三级| 久久亚洲综合网| 国产极品人妖在线观看| 欧美一级高清大全免费观看| 亚洲精品国产精品国自| 国产精品美女久久久| 成人在线视频电影| 国产剧情在线| 欧美片网站yy| 奇米网一区二区| 日韩和欧美一区二区三区| 精品综合在线| 深夜在线视频| 亚洲精品av在线| 国产第100页| 国产99久久精品| 无码人妻精品一区二区蜜桃百度| 日韩欧美三区| 中文字幕日韩欧美在线视频| youjizz在线视频| 99re这里只有精品首页| 国产特级黄色大片| 亚洲素人在线| 欧美一级片久久久久久久| 亚洲 欧美 精品| 精品久久久精品| 黄色国产在线观看| 性色一区二区| 奇米精品在线| 高清欧美日韩| 久久网福利资源网站| 国产老妇伦国产熟女老妇视频| 中文字幕一区二区三区av| 亚洲 激情 在线| 99热精品久久| 91九色偷拍| 久草在线新免费首页资源站| 精品裸体舞一区二区三区| 成人免费看片98| 99精品久久只有精品| 黄色影院一级片| 久草精品在线| 91精品视频在线| 色呦呦在线视频| 日韩精品一区二区三区第95| 性色av免费观看| 国产精品乱人伦中文| 想看黄色一级片| 亚洲无线视频| 欧美一区国产一区| 色综合视频一区二区三区日韩| 久久久av电影| 色婷婷视频在线| 日本韩国一区二区三区| 四虎影院中文字幕| 成人99免费视频| 四季av一区二区| 欧美激情麻豆| 欧洲精品一区色| 国产精品美女久久久久人| 欧美精品videosex极品1| 五十路在线视频| 欧美日韩亚洲综合一区| 久一区二区三区| 欧美激情一区在线观看| 久久精品无码一区二区三区毛片| 国产精品美女久久久浪潮软件| 亚洲三区在线观看| 极品束缚调教一区二区网站| 国产精品麻豆va在线播放| 国产乱妇乱子在线播视频播放网站| 亚洲欧美精品一区| 国产乱色精品成人免费视频| 偷拍亚洲欧洲综合| 顶级黑人搡bbw搡bbbb搡| av一本久道久久综合久久鬼色| 国产又大又黄又猛| 亚洲精品免费观看| 桥本有菜av在线| 日韩美女精品| 91pron在线| 欧美日韩精品一区二区三区视频| 欧美精品aaa| 日韩在线资源| 亚洲欧美综合区自拍另类| 成人午夜免费在线观看| 欧美日韩精品三区| 成人公开免费视频| 亚洲国产精品久久人人爱| 国产一区二区三区视频播放| 久久综合精品国产一区二区三区 | 不卡一区二区三区四区| 日本中文字幕精品—区二区| 午夜综合激情| 精品久久久久久无码中文野结衣| 欧美高清视频手机在在线| 欧美日韩精品久久| 欧美亚洲色图校园春色| 超碰在线观看97| 999精品视频在线观看| 国产成人aa精品一区在线播放| gogo高清在线播放免费| 色综合久久88| av香蕉成人| 久久夜色精品国产| 美女羞羞视频在线观看| 最近2019年手机中文字幕| 免费看男男www网站入口在线| 精品国产一区二区三区四区四 | 亚洲欧美制服第一页| 日本xxxxwww| 亚洲精品在线观| 国产成人精品免费看视频| 欧美久久高跟鞋激| 中文字幕一二区| 欧美日韩一区二区三区四区五区| 国产精品尤物视频| 在线观看91精品国产入口| 四虎影院在线免费播放| 色婷婷亚洲精品| 无码人妻精品一区二区三区蜜桃91| 精品av在线播放| 国产区在线观看视频| 精品福利在线看| 日本va欧美va国产激情| 欧美日韩一区二区在线 | 免费在线中文字幕| 欧美黑人巨大xxx极品| 七七成人影院| 97视频网站入口| 伊人久久综合一区二区| 浅井舞香一区二区| 久久91视频| 亚洲va久久久噜噜噜| 日韩中文一区二区| 官网99热精品| 性欧美xxxx免费岛国不卡电影| 免费在线观看91| 欧美综合在线视频观看 | av毛片在线免费| 欧美黑人狂野猛交老妇| 老色鬼在线视频| 国产精品国产亚洲伊人久久| 开心久久婷婷综合中文字幕| 成人免费在线网址| 成人偷拍自拍| 日本精品一区| 97精品在线| 国产资源在线免费观看| 午夜在线一区| 国产三级精品三级在线| 成人手机在线视频| 在线观看国产精品一区| 亚洲日本一区二区| 免费日韩一级片| 欧美日韩一卡二卡三卡| 精品国自产在线观看| 日韩精品亚洲精品| 国产一二三区在线观看| 韩国精品久久久999| 成人午夜一级| 成人激情直播| jiujiure精品视频播放| 国产 国语对白 露脸| 欧美一级二区| www.桃色.com| 2023国产一二三区日本精品2022| 天天色天天综合| 污片在线观看一区二区| 91精品国产乱码久久久久| 亚洲国产精品va在线看黑人| 99免在线观看免费视频高清| 久久久久久久一区二区| 91大神在线观看线路一区| 国产区二精品视| 欧美成人激情| 两根大肉大捧一进一出好爽视频| 国产在线不卡一区| 人妻少妇一区二区| 一区二区视频在线| 国产91av在线播放| 亚洲国产福利在线| 国产网友自拍视频导航网站在线观看| 国模视频一区二区| 国产一区二区视频在线看| 欧美一区二区三区在线播放| 国产精品videosex极品| 亚欧美在线观看| 久久精品亚洲精品国产欧美| 国产真实的和子乱拍在线观看| 欧美乱妇一区二区三区不卡视频| 头脑特工队2免费完整版在线观看| 久久精品99久久香蕉国产色戒| 中文在线8资源库| 电影午夜精品一区二区三区| 91综合网人人| 欧美精品无码一区二区三区| 91亚洲精品久久久蜜桃网站 | 午夜视频在线观看免费视频| 欧美一级大片视频| 国产精品视频3p| 欧美日韩视频免费| 国产制服丝袜一区| 少妇高潮惨叫久久久久| 欧美视频一区在线观看| 麻豆国产在线播放| 欧美性在线视频| 欧美绝顶高潮抽搐喷水合集| 久久久久久免费看| 成人免费毛片片v| 欧美三级免费看| 日韩欧美一级在线播放| 亚洲欧美成人影院| 亚洲在线视频观看| 欧美一区精品| 久久久久中文字幕亚洲精品| 亚洲最大色网站| 好男人www在线视频| 色综合色综合久久综合频道88| 精品三级国产| 欧美 日韩 国产精品| 国产不卡免费视频| 久草网在线观看| 欧美精品一区二区三区在线| 国产黄色大片在线观看| 国产尤物99| 欧美一级专区| 免费看黄色三级| 欧美日韩免费一区二区三区视频 | 精品一区在线| 国产福利一区视频| 中文av一区二区| 国产绳艺sm调教室论坛| 欧美日本高清一区| 激情小说亚洲图片| 熟女性饥渴一区二区三区| 国产欧美精品一区aⅴ影院| 中文字幕乱码人妻二区三区| www国产精品视频| 青草伊人久久| 国产精品成人久久电影| 91视视频在线直接观看在线看网页在线看| www成人在线| 最近2019中文字幕一页二页| 成人综合日日夜夜| 亚洲精品无码国产| 久久久青草青青国产亚洲免观| 中文字幕欧美人妻精品| 久久影院中文字幕| 久久丝袜视频| 蜜臀av免费观看| 一区二区成人在线| 日本一本草久在线中文| 国产精品一区专区欧美日韩| 一精品久久久| 中文字幕在线观看网址| 欧美精品xxxxbbbb| xxx.xxx欧美| 亚洲视频sss| av动漫一区二区| 亚洲天堂2021av| 91国内揄拍国内精品对白| 精品亚洲成人| 深夜视频在线观看| 在线观看免费亚洲| 久久国产精品黑丝| 亚洲欧洲中文| av网站免费线看精品| 国产有码在线观看| 69av在线视频| 欧美在线二区| 欧美xxxx精品| 亚洲精品电影网| 日韩三级网址| 奇米影视四色在线|