最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java本地高性能緩存的幾種常見實(shí)現(xiàn)方式

 更新時(shí)間:2024年07月06日 09:48:10   作者:吳名氏  
在Java中緩存是一種常用的性能優(yōu)化技術(shù),用于在應(yīng)用程序中加速訪問和查詢數(shù)據(jù)的速度,下面這篇文章主要給大家介紹了關(guān)于Java本地高性能緩存的幾種常見實(shí)現(xiàn)方式,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

Java緩存技術(shù)可分為遠(yuǎn)端緩存和本地緩存,遠(yuǎn)端緩存常用的方案有著名的redis和memcache,而本地緩存的代表技術(shù)主要有HashMap,Guava Cache,Caffeine和Encahche。本篇博文僅覆蓋了本地緩存,且突出探討高性能的本地緩存。

本篇博文將首先介紹常見的本地緩存技術(shù),對(duì)本地緩存有個(gè)大概的了解;其次介紹本地緩存中號(hào)稱性能最好的Cache,可以探討看看到底有多好?怎么做到這么好?最后通過幾個(gè)實(shí)戰(zhàn)樣例,在日常工作中應(yīng)用高性能的本地緩存。

一、 Java本地緩存技術(shù)介紹

1.1 使用List集合contains方法循環(huán)遍歷(有序) 1.1 HashMap

通過Map的底層方式,直接將需要緩存的對(duì)象放在內(nèi)存中。

  • 優(yōu)點(diǎn):簡(jiǎn)單粗暴,不需要引入第三方包,比較適合一些比較簡(jiǎn)單的場(chǎng)景。

  • 缺點(diǎn):沒有緩存淘汰策略,定制化開發(fā)成本高。

public class LRUCache extends LinkedHashMap {

    /**
     * 可重入讀寫鎖,保證并發(fā)讀寫安全性
     */
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private Lock readLock = readWriteLock.readLock();
    private Lock writeLock = readWriteLock.writeLock();

    /**
     * 緩存大小限制
     */
    private int maxSize;

    public LRUCache(int maxSize) {
        super(maxSize + 1, 1.0f, true);
        this.maxSize = maxSize;
    }

    @Override
    public Object get(Object key) {
        readLock.lock();
        try {
            return super.get(key);
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public Object put(Object key, Object value) {
        writeLock.lock();
        try {
            return super.put(key, value);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return this.size() > maxSize;
    }
}

1.2 Guava Cache

Guava Cache是由Google開源的基于LRU替換算法的緩存技術(shù)。但Guava Cache由于被下面即將介紹的Caffeine全面超越而被取代,因此不特意編寫示例代碼了,有興趣的讀者可以訪問Guava Cache主頁(yè)。

  • 優(yōu)點(diǎn):支持最大容量限制,兩種過期刪除策略(插入時(shí)間和訪問時(shí)間),支持簡(jiǎn)單的統(tǒng)計(jì)功能。

  • 缺點(diǎn):springboot2和spring5都放棄了對(duì)Guava Cache的支持。

1.3 Caffeine

Caffeine采用了W-TinyLFU(LUR和LFU的優(yōu)點(diǎn)結(jié)合)開源的緩存技術(shù)。緩存性能接近理論最優(yōu),屬于是Guava Cache的增強(qiáng)版。

public class CaffeineCacheTest {

    public static void main(String[] args) throws Exception {
        //創(chuàng)建guava cache
        Cache<String, String> loadingCache = Caffeine.newBuilder()
                //cache的初始容量
                .initialCapacity(5)
                //cache最大緩存數(shù)
                .maximumSize(10)
                //設(shè)置寫緩存后n秒鐘過期
                .expireAfterWrite(17, TimeUnit.SECONDS)
                //設(shè)置讀寫緩存后n秒鐘過期,實(shí)際很少用到,類似于expireAfterWrite
                //.expireAfterAccess(17, TimeUnit.SECONDS)
                .build();
        String key = "key";
        // 往緩存寫數(shù)據(jù)
        loadingCache.put(key, "v");

        // 獲取value的值,如果key不存在,獲取value后再返回
        String value = loadingCache.get(key, CaffeineCacheTest::getValueFromDB);

        // 刪除key
        loadingCache.invalidate(key);
    }

    private static String getValueFromDB(String key) {
        return "v";
    }
}

1.4 Encache

Ehcache是一個(gè)純java的進(jìn)程內(nèi)緩存框架,具有快速、精干的特點(diǎn)。是hibernate默認(rèn)的cacheprovider。

  • 優(yōu)點(diǎn):支持多種緩存淘汰算法,包括LFU,LRU和FIFO;緩存支持堆內(nèi)緩存,堆外緩存和磁盤緩存;支持多種集群方案,解決數(shù)據(jù)共享問題。

  • 缺點(diǎn):性能比Caffeine差

public class EncacheTest {

    public static void main(String[] args) throws Exception {
        // 聲明一個(gè)cacheBuilder
        CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
                .withCache("encacheInstance", CacheConfigurationBuilder
                        //聲明一個(gè)容量為20的堆內(nèi)緩存
                        .newCacheConfigurationBuilder(String.class,String.class, ResourcePoolsBuilder.heap(20)))
                .build(true);
        // 獲取Cache實(shí)例
        Cache<String,String> myCache =  cacheManager.getCache("encacheInstance", String.class, String.class);
        // 寫緩存
        myCache.put("key","v");
        // 讀緩存
        String value = myCache.get("key");
        // 移除換粗
        cacheManager.removeCache("myCache");
        cacheManager.close();
    }
}

 在Caffeine的官網(wǎng)介紹中,Caffeine在性能和功能上都與其他幾種方案相比具有優(yōu)勢(shì),因此接下來主要探討Caffeine的性能和實(shí)現(xiàn)原理。

二、高性能緩存Caffeine

2.1 緩存類型

2.1.1 Cache

Cache<Key, Graph> cache = Caffeine.newBuilder()
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .maximumSize(10_000)
    .build();

// 查找一個(gè)緩存元素, 沒有查找到的時(shí)候返回null
Graph graph = cache.getIfPresent(key);
// 查找緩存,如果緩存不存在則生成緩存元素,  如果無法生成則返回null
graph = cache.get(key, k -> createExpensiveGraph(key));
// 添加或者更新一個(gè)緩存元素
cache.put(key, graph);
// 移除一個(gè)緩存元素
cache.invalidate(key);

Cache 接口提供了顯式搜索查找、更新和移除緩存元素的能力。當(dāng)緩存的元素?zé)o法生成或者在生成的過程中拋出異常而導(dǎo)致生成元素失敗,cache.get 也許會(huì)返回 null 。

2.1.2 Loading Cache

LoadingCache<Key, Graph> cache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .build(key -> createExpensiveGraph(key));

// 查找緩存,如果緩存不存在則生成緩存元素,  如果無法生成則返回null
Graph graph = cache.get(key);
// 批量查找緩存,如果緩存不存在則生成緩存元素
Map<Key, Graph> graphs = cache.getAll(keys);

一個(gè)LoadingCache是一個(gè)Cache 附加上 CacheLoader能力之后的緩存實(shí)現(xiàn)。
如果緩存不錯(cuò)在,則會(huì)通過CacheLoader.load來生成對(duì)應(yīng)的緩存元素。

2.1.3 Loading Cache 2.1.3 Async Cache

AsyncCache<Key, Graph> cache = Caffeine.newBuilder()
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .maximumSize(10_000)
    .buildAsync();

// 查找一個(gè)緩存元素, 沒有查找到的時(shí)候返回null
CompletableFuture<Graph> graph = cache.getIfPresent(key);
// 查找緩存元素,如果不存在,則異步生成
graph = cache.get(key, k -> createExpensiveGraph(key));
// 添加或者更新一個(gè)緩存元素
cache.put(key, graph);
// 移除一個(gè)緩存元素
cache.synchronous().invalidate(key);

AsyncCache就是Cache的異步形式,提供了Executor生成緩存元素并返回CompletableFuture的能力。默認(rèn)的線程池實(shí)現(xiàn)是 ForkJoinPool.commonPool() ,當(dāng)然你也可以通過覆蓋并實(shí)現(xiàn) Caffeine.executor(Executor)方法來自定義你的線程池選擇。

2.1.4 Async Loading Cache

AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    // 你可以選擇: 去異步的封裝一段同步操作來生成緩存元素
    .buildAsync(key -> createExpensiveGraph(key));
    // 你也可以選擇: 構(gòu)建一個(gè)異步緩存元素操作并返回一個(gè)future
    .buildAsync((key, executor) -> createExpensiveGraphAsync(key, executor));

// 查找緩存元素,如果其不存在,將會(huì)異步進(jìn)行生成
CompletableFuture<Graph> graph = cache.get(key);
// 批量查找緩存元素,如果其不存在,將會(huì)異步進(jìn)行生成
CompletableFuture<Map<Key, Graph>> graphs = cache.getAll(keys);

AsyncLoadingCache就是LoadingCache的異步形式,提供了異步load生成緩存元素的功能。

2.2 驅(qū)逐策略

  • 基于容量

// 基于緩存內(nèi)的元素個(gè)數(shù)進(jìn)行驅(qū)逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .maximumSize(10_000)
    .build(key -> createExpensiveGraph(key));

// 基于緩存內(nèi)元素權(quán)重進(jìn)行驅(qū)逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .maximumWeight(10_000)
    .weigher((Key key, Graph graph) -> graph.vertices().size())
    .build(key -> createExpensiveGraph(key));
  • 基于時(shí)間

// 基于固定的過期時(shí)間驅(qū)逐策略
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .expireAfterAccess(5, TimeUnit.MINUTES)
    .build(key -> createExpensiveGraph(key));
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .build(key -> createExpensiveGraph(key));

// 基于不同的過期驅(qū)逐策略
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .expireAfter(new Expiry<Key, Graph>() {
      public long expireAfterCreate(Key key, Graph graph, long currentTime) {
        // Use wall clock time, rather than nanotime, if from an external resource
        long seconds = graph.creationDate().plusHours(5)
            .minus(System.currentTimeMillis(), MILLIS)
            .toEpochSecond();
        return TimeUnit.SECONDS.toNanos(seconds);
      }
      public long expireAfterUpdate(Key key, Graph graph, 
          long currentTime, long currentDuration) {
        return currentDuration;
      }
      public long expireAfterRead(Key key, Graph graph,
          long currentTime, long currentDuration) {
        return currentDuration;
      }
    })
    .build(key -> createExpensiveGraph(key));
  • 基于引用

// 當(dāng)key和緩存元素都不再存在其他強(qiáng)引用的時(shí)候驅(qū)逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .weakKeys()
    .weakValues()
    .build(key -> createExpensiveGraph(key));

// 當(dāng)進(jìn)行GC的時(shí)候進(jìn)行驅(qū)逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .softValues()
    .build(key -> createExpensiveGraph(key));

2.3 刷新機(jī)制

LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .maximumSize(10_000)
    .refreshAfterWrite(1, TimeUnit.MINUTES)
    .build(key -> createExpensiveGraph(key));

只有在LoadingCache中可以使用刷新策略,與驅(qū)逐不同的是,在刷新的時(shí)候如果查詢緩存元素,其舊值將仍被返回,直到該元素的刷新完畢后結(jié)束后才會(huì)返回刷新后的新值。

2.4 統(tǒng)計(jì)

Cache<Key, Graph> graphs = Caffeine.newBuilder()
    .maximumSize(10_000)
    .recordStats()
    .build();

通過使用Caffeine.recordStats()方法可以打開數(shù)據(jù)收集功能。Cache.stats()方法將會(huì)返回一個(gè)CacheStats對(duì)象,其將會(huì)含有一些統(tǒng)計(jì)指標(biāo),比如:

  • hitRate(): 查詢緩存的命中率

  • evictionCount(): 被驅(qū)逐的緩存數(shù)量

  • averageLoadPenalty(): 新值被載入的平均耗時(shí)

配合SpringBoot提供的RESTful Controller,能很方便的查詢Cache的使用情況。

三、Caffeine在SpringBoot的實(shí)戰(zhàn)

按照Caffeine Github官網(wǎng)文檔的描述,Caffeine是基于Java8的高性能緩存庫(kù)。并且在Spring5(SpringBoot2.x)官方放棄了Guava,而使用了性能更優(yōu)秀的Caffeine作為默認(rèn)的緩存方案。

SpringBoot使用Caffeine有兩種方式:

  • 方式一:直接引入Caffeine依賴,然后使用Caffeine的函數(shù)實(shí)現(xiàn)緩存

  • 方式二:引入Caffeine和Spring Cache依賴,使用SpringCache注解方法實(shí)現(xiàn)緩存
    下面分別介紹兩種使用方式。

方式一:使用Caffeine依賴

首先引入maven相關(guān)依賴:

<dependency>  
  <groupId>com.github.ben-manes.caffeine</groupId>  
    <artifactId>caffeine</artifactId>  
</dependency>

其次,設(shè)置緩存的配置選項(xiàng)

@Configuration
public class CacheConfig {

    @Bean
    public Cache<String, Object> caffeineCache() {
        return Caffeine.newBuilder()
                // 設(shè)置最后一次寫入或訪問后經(jīng)過固定時(shí)間過期
                .expireAfterWrite(60, TimeUnit.SECONDS)
                // 初始的緩存空間大小
                .initialCapacity(100)
                // 緩存的最大條數(shù)
                .maximumSize(1000)
                .build();
    }

}

最后給服務(wù)添加緩存功能

@Slf4j
@Service
public class UserInfoServiceImpl {

    /**
     * 模擬數(shù)據(jù)庫(kù)存儲(chǔ)數(shù)據(jù)
     */
    private HashMap<Integer, UserInfo> userInfoMap = new HashMap<>();

    @Autowired
    Cache<String, Object> caffeineCache;

    public void addUserInfo(UserInfo userInfo) {
        userInfoMap.put(userInfo.getId(), userInfo);
        // 加入緩存
        caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);
    }

    public UserInfo getByName(Integer id) {
        // 先從緩存讀取
        caffeineCache.getIfPresent(id);
        UserInfo userInfo = (UserInfo) caffeineCache.asMap().get(String.valueOf(id));
        if (userInfo != null){
            return userInfo;
        }
        // 如果緩存中不存在,則從庫(kù)中查找
        userInfo = userInfoMap.get(id);
        // 如果用戶信息不為空,則加入緩存
        if (userInfo != null){
            caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);
        }
        return userInfo;
    }

    public UserInfo updateUserInfo(UserInfo userInfo) {
        if (!userInfoMap.containsKey(userInfo.getId())) {
            return null;
        }
        // 取舊的值
        UserInfo oldUserInfo = userInfoMap.get(userInfo.getId());
        // 替換內(nèi)容
        if (!StringUtils.isEmpty(oldUserInfo.getAge())) {
            oldUserInfo.setAge(userInfo.getAge());
        }
        if (!StringUtils.isEmpty(oldUserInfo.getName())) {
            oldUserInfo.setName(userInfo.getName());
        }
        if (!StringUtils.isEmpty(oldUserInfo.getSex())) {
            oldUserInfo.setSex(userInfo.getSex());
        }
        // 將新的對(duì)象存儲(chǔ),更新舊對(duì)象信息
        userInfoMap.put(oldUserInfo.getId(), oldUserInfo);
        // 替換緩存中的值
        caffeineCache.put(String.valueOf(oldUserInfo.getId()),oldUserInfo);
        return oldUserInfo;
    }

    @Override
    public void deleteById(Integer id) {
        userInfoMap.remove(id);
        // 從緩存中刪除
        caffeineCache.asMap().remove(String.valueOf(id));
    }

}

方式二:使用Spring Cache注解

首先引入maven相關(guān)依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

其次,配置緩存管理類

@Configuration  
public class CacheConfig {  
  
    /**  
     * 配置緩存管理器  
     *  
     * @return 緩存管理器  
     */  
    @Bean("caffeineCacheManager")  
    public CacheManager cacheManager() {  
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();  
        cacheManager.setCaffeine(Caffeine.newBuilder()  
                // 設(shè)置最后一次寫入或訪問后經(jīng)過固定時(shí)間過期  
                .expireAfterAccess(60, TimeUnit.SECONDS)  
                // 初始的緩存空間大小  
                .initialCapacity(100)  
                // 緩存的最大條數(shù)  
                .maximumSize(1000));  
        return cacheManager;  
    }  
  
}

最后給服務(wù)添加緩存功能

@Slf4j
@Service
@CacheConfig(cacheNames = "caffeineCacheManager")
public class UserInfoServiceImpl {

    /**
     * 模擬數(shù)據(jù)庫(kù)存儲(chǔ)數(shù)據(jù)
     */
    private HashMap<Integer, UserInfo> userInfoMap = new HashMap<>();

    @CachePut(key = "#userInfo.id")
    public void addUserInfo(UserInfo userInfo) {
        userInfoMap.put(userInfo.getId(), userInfo);
    }

    @Cacheable(key = "#id")
    public UserInfo getByName(Integer id) {
        return userInfoMap.get(id);
    }

    @CachePut(key = "#userInfo.id")
    public UserInfo updateUserInfo(UserInfo userInfo) {
        if (!userInfoMap.containsKey(userInfo.getId())) {
            return null;
        }
        // 取舊的值
        UserInfo oldUserInfo = userInfoMap.get(userInfo.getId());
        // 替換內(nèi)容
        if (!StringUtils.isEmpty(oldUserInfo.getAge())) {
            oldUserInfo.setAge(userInfo.getAge());
        }
        if (!StringUtils.isEmpty(oldUserInfo.getName())) {
            oldUserInfo.setName(userInfo.getName());
        }
        if (!StringUtils.isEmpty(oldUserInfo.getSex())) {
            oldUserInfo.setSex(userInfo.getSex());
        }
        // 將新的對(duì)象存儲(chǔ),更新舊對(duì)象信息
        userInfoMap.put(oldUserInfo.getId(), oldUserInfo);
        // 返回新對(duì)象信息
        return oldUserInfo;
    }

    @CacheEvict(key = "#id")
    public void deleteById(Integer id) {
        userInfoMap.remove(id);
    }

}

總結(jié) 

到此這篇關(guān)于Java本地高性能緩存的幾種常見實(shí)現(xiàn)方式的文章就介紹到這了,更多相關(guān)Java本地高性能緩存實(shí)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • logback的使用和logback.xml詳解(小結(jié))

    logback的使用和logback.xml詳解(小結(jié))

    Logback是由log4j創(chuàng)始人設(shè)計(jì)的另一個(gè)開源日志組件,這篇文章主要介紹了logback的使用和logback.xml詳解(小結(jié)),非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2018-11-11
  • 劍指Offer之Java算法習(xí)題精講排列與N叉樹

    劍指Offer之Java算法習(xí)題精講排列與N叉樹

    跟著思路走,之后從簡(jiǎn)單題入手,反復(fù)去看,做過之后可能會(huì)忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會(huì)發(fā)現(xiàn)質(zhì)的變化
    2022-03-03
  • j2Cache線上異常排查問題解決記錄分析

    j2Cache線上異常排查問題解決記錄分析

    這篇文章主要為大家介紹了關(guān)于j2Cache線上異常排查的問題解決記錄分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-02-02
  • Spring使用注解進(jìn)行引用類型的自動(dòng)裝配逐步分析

    Spring使用注解進(jìn)行引用類型的自動(dòng)裝配逐步分析

    自動(dòng)裝配是springboot的核心,一般提到自動(dòng)裝配就會(huì)和springboot聯(lián)系在一起。實(shí)際上Spring Framework早就實(shí)現(xiàn)了這個(gè)功能。Spring Boot只是在其基礎(chǔ)上,通過SPI的方式,做了進(jìn)一步優(yōu)化
    2023-03-03
  • SpringBoot集成ShardingSphere-JDBC實(shí)現(xiàn)分庫(kù)分表

    SpringBoot集成ShardingSphere-JDBC實(shí)現(xiàn)分庫(kù)分表

    本文主要介紹了SpringBoot集成ShardingSphere-JDBC實(shí)現(xiàn)分庫(kù)分表,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-07-07
  • 在netty中使用native傳輸協(xié)議的方法

    在netty中使用native傳輸協(xié)議的方法

    這篇文章主要介紹了在netty中使用native傳輸協(xié)議,這里我們只以Kqueue為例介紹了netty中native傳輸協(xié)議的使用,需要的朋友可以參考下
    2022-05-05
  • Java之Algorithm_analysis案例詳解

    Java之Algorithm_analysis案例詳解

    這篇文章主要介紹了Java之Algorithm_analysis案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • Java多線程實(shí)現(xiàn)Runnable方式

    Java多線程實(shí)現(xiàn)Runnable方式

    這篇文章主要為大家詳細(xì)介紹了Java多線程如何實(shí)現(xiàn)Runnable方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • 將java程序打成jar包在cmd命令行下執(zhí)行的方法

    將java程序打成jar包在cmd命令行下執(zhí)行的方法

    這篇文章主要給大家介紹了關(guān)于將java程序打成jar包在cmd命令行下執(zhí)行的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-01-01
  • 淺談SpringBoot之開啟數(shù)據(jù)庫(kù)遷移的FlyWay使用

    淺談SpringBoot之開啟數(shù)據(jù)庫(kù)遷移的FlyWay使用

    這篇文章主要介紹了淺談SpringBoot之開啟數(shù)據(jù)庫(kù)遷移的FlyWay使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-01-01

最新評(píng)論

宣恩县| 乌苏市| 仙居县| 松潘县| 砚山县| 娱乐| 隆林| 禄劝| 伊通| 安多县| 福泉市| 札达县| 玉门市| 阿克陶县| 左权县| 陵水| 拉孜县| 奉新县| 涿鹿县| 获嘉县| 宿迁市| 普定县| 平和县| 隆尧县| 昌乐县| 中超| 临泉县| 民丰县| 甘南县| 大理市| 宁都县| 玉林市| 台湾省| 凭祥市| 古浪县| 高邮市| 乐山市| 五家渠市| 临江市| 泉州市| 喀喇沁旗|