Apache Ignite

什麼是Apache Ignite?
       Ignite是一個以內存爲中心的分佈式數據庫、緩存和處理平臺,可以在PB級數據中,以內存級的速度進行事務性、分析性以及流式負載的處理。

      上述引用了Ignite的官方介紹,通俗來講,Ignite就是一個內存數據庫,它包括了很多特性,它既是一個分佈式緩存,也是一個分佈式數據庫,同時也支持一定程度的ACID事務。

關鍵字:固化內存、並置處理

固化內存
        Ignite的固化內存組件不僅僅將內存作爲一個緩存層,還視爲一個全功能的存儲層。這意味着可以按需將持久化打開或者關閉。如果持久化關閉,那麼Ignite就可以作爲一個分佈式的內存數據庫或者內存數據網格,這完全取決於使用SQL和鍵-值API的喜好。如果持久化打開,那麼Ignite就成爲一個分佈式的,可水平擴展的數據庫,它會保證完整的數據一致性以及集羣故障的可恢復能力。

並置處理
       Ignite是一個分佈式系統,因此,有能力將數據和數據以及數據和計算進行並置就變得非常重要,這會避免分佈式數據噪聲。當執行分佈式SQL關聯時數據的並置就變得非常的重要。Ignite還支持將用戶的邏輯(函數,lambda等)直接發到數據所在的節點然後在本地進行數據的運算。

Ingite和Spark有什麼區別?
       Ignite 是一個基於內存的計算系統,也就是把內存做爲主要的存儲設備。Spark 則是在處理時使用內存。相比之下,Ingite的索引更快,提取時間減少,避免了序列/反序列等優點,使前者這種內存優先的方法更快。

        Ignite提供了一個Spark RDD抽象的實現,他可以容易地在內存中跨越多個Spark作業共享狀態,在跨越不同Spark作業、工作節點或者應用時,IgniteRDD爲內存中的相同數據提供了一個共享的、可變的視圖,而原生的SparkRDD無法在Spark作業或者應用之間進行共享。 
1. 獲得真正的可擴展的內存級性能,避免數據源和Spark工作節點和應用之間的數據移動 
2. 提升DataFrame和SQL的性能 
3. 在Spark作業之間更容易地共享狀態和數據

 

如何使用Ingite?
 

啓動Ingite集羣 
從官網下載zip格式壓縮包 
解壓到系統中的一個安裝文件夾,如apache-ignite-fabric-2.6.0-bin 
配置IGNITE_HOME環境變量 
linux執行命令:

bin/ignite.sh

此時將啓動Ingite,當最後輸出下面信息時,則說明啓動成功:

[20:47:15] Ignite node started OK (id=60d15c51)
[20:47:15] Topology snapshot [ver=1, servers=1, clients=0, CPUs=4, offheap=3.2GB, heap=3.5GB]

使用maven啓動

<dependency>
    <groupId>org.apache.ignite</groupId>
    <artifactId>ignite-core</artifactId>
    <version>${ignite.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.ignite</groupId>
    <artifactId>ignite-spring</artifactId>
    <version>${ignite.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.ignite</groupId>
    <artifactId>ignite-indexing</artifactId>
    <version>${ignite.version}</version>
</dependency>

目前ignite最新版本爲2.6

Ingite集羣知識
Ignite具有非常先進的集羣能力,包括邏輯集羣組和自動發現。 
Ignite節點之間會自動發現對方,這有助於必要時擴展集羣,而不需要重啓整個集羣。開發者可以利用Ignite的混合雲支持,允許公有云(比如AWS)和私有云之間建立連接,向他們提供兩者的好處。 
Ingite有兩種發現機制,分別是TCP/IP和Zookeeper。總的來說對於小集羣,使用默認的TCP/IP發現方式,對於成百上千的大集羣,則建議使用Zookeeper。 


 

作爲關係型數據庫使用
創建表
可以通過控制檯和jdbc兩種方式執行sql語句,支持H2的語法。

控制檯 
1. 執行ignite.sh啓動集羣 
2. 啓動sqlline

sqlline.sh --color=true --verbose=true -u jdbc:ignite:thin://127.0.0.1/

執行腳本 用!sql +sql 語句即可

!sql CREATE TABLE City (id LONG PRIMARY KEY, name VARCHAR) WITH "template=replicated";
!sql CREATE TABLE Person (id LONG, name VARCHAR, city_id LONG, PRIMARY KEY (id, city_id)) WITH "backups=1, affinityKey=city_id";
!sql CREATE INDEX idx_city_name ON City (name);
!sql CREATE INDEX idx_person_name ON Person (name);

jdbc

// 註冊JDBC driver.
Class.forName("org.apache.ignite.IgniteJdbcThinDriver");

// 打開 JDBC connection.
Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/");

// 創建表
try (Statement stmt = conn.createStatement()) {

    // 創建基於複製模式的City表
    stmt.executeUpdate("CREATE TABLE City (" + 
    " id LONG PRIMARY KEY, name VARCHAR) " +
    " WITH \"template=replicated\"");

    // 創建基於分片模式,備份數爲1的Person表
    stmt.executeUpdate("CREATE TABLE Person (" +
    " id LONG, name VARCHAR, city_id LONG, " +
    " PRIMARY KEY (id, city_id)) " +
    " WITH \"backups=1, affinityKey=city_id\"");

    // 創建City表的索引
    stmt.executeUpdate("CREATE INDEX idx_city_name ON City (name)");

    // 創建Person表的索引
    stmt.executeUpdate("CREATE INDEX idx_person_name ON Person (name)");

插入數據
控制檯

!sql INSERT INTO City (id, name) VALUES (1, 'Forest Hill');
!sql INSERT INTO City (id, name) VALUES (2, 'Denver');
!sql INSERT INTO City (id, name) VALUES (3, 'St. Petersburg');

!sql INSERT INTO Person (id, name, city_id) VALUES (1, 'John Doe', 3);
!sql INSERT INTO Person (id, name, city_id) VALUES (2, 'Jane Roe', 2);
!sql INSERT INTO Person (id, name, city_id) VALUES (3, 'Mary Major', 1);
!sql INSERT INTO Person (id, name, city_id) VALUES (4, 'Richard Miles', 2); 

jdbc

Class.forName("org.apache.ignite.IgniteJdbcThinDriver");

Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/");

try (PreparedStatement stmt =
conn.prepareStatement("INSERT INTO City (id, name) VALUES (?, ?)")) {

    stmt.setLong(1, 1L);
    stmt.setString(2, "Forest Hill");
    stmt.executeUpdate();

    stmt.setLong(1, 2L);
    stmt.setString(2, "Denver");
    stmt.executeUpdate();

    stmt.setLong(1, 3L);
    stmt.setString(2, "St. Petersburg");
    stmt.executeUpdate();
}

try (PreparedStatement stmt =
conn.prepareStatement("INSERT INTO Person (id, name, city_id) VALUES (?, ?, ?)")) {

    stmt.setLong(1, 1L);
    stmt.setString(2, "John Doe");
    stmt.setLong(3, 3L);
    stmt.executeUpdate();

    stmt.setLong(1, 2L);
    stmt.setString(2, "Jane Roe");
    stmt.setLong(3, 2L);
    stmt.executeUpdate();

    stmt.setLong(1, 3L);
    stmt.setString(2, "Mary Major");
    stmt.setLong(3, 1L);
    stmt.executeUpdate();

    stmt.setLong(1, 4L);
    stmt.setString(2, "Richard Miles");
    stmt.setLong(3, 2L);
    stmt.executeUpdate();
}

查詢數據
控制檯

!sql SELECT p.name, c.name FROM Person p, City c WHERE p.city_id = c.id;

jdbc

Class.forName("org.apache.ignite.IgniteJdbcThinDriver");

Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/");

// 使用標準的sql獲取數據
try (Statement stmt = conn.createStatement()) {
    try (ResultSet rs =
    stmt.executeQuery("SELECT p.name, c.name " +
    " FROM Person p, City c " +
    " WHERE p.city_id = c.id")) {

      while (rs.next())
         System.out.println(rs.getString(1) + ", " + rs.getString(2));
    }
}

輸出結果
 

Mary Major, Forest Hill
Jane Roe, Denver
Richard Miles, Denver
John Doe, St. Petersburg

作爲並置處理引擎
 

try (Ignite ignite = Ignition.start()) {
  Collection<IgniteCallable<Integer>> calls = new ArrayList<>();
  // 將這句字符串按照空格分組,並將任務分派到各個節點中計算
  for (final String word : "Count characters using callable".split(" "))
    calls.add(word::length);
  // 分派
  Collection<Integer> res = ignite.compute().call(calls);
  // 將各個節點統計後的結果彙總
  int sum = res.stream().mapToInt(Integer::intValue).sum();
    System.out.println("Total number of characters is '" + sum + "'.");
}

輸出

Total number of characters is '28'.

作爲Data Grid(數據網格)應用
鍵值對存放及讀取

 

try (Ignite ignite = Ignition.start()) {
    IgniteCache<Integer, String> cache = ignite.getOrCreateCache("myCacheName");

    // Store keys in cache (values will end up on different cache nodes).
    for (int i = 0; i < 10; i++)
        cache.put(i, Integer.toString(i));

    for (int i = 0; i < 10; i++)
        System.out.println("Got [key=" + i + ", val=" + cache.get(i) + ']');
}

原子操作
 

// Put-if-absent which returns previous value.
Integer oldVal = cache.getAndPutIfAbsent("Hello", 11);

// Put-if-absent which returns boolean success flag.
boolean success = cache.putIfAbsent("World", 22);

// Replace-if-exists operation (opposite of getAndPutIfAbsent), returns previous value.
oldVal = cache.getAndReplace("Hello", 11);

// Replace-if-exists operation (opposite of putIfAbsent), returns boolean success flag.
success = cache.replace("World", 22);

// Replace-if-matches operation.
success = cache.replace("World", 2, 22);

// Remove-if-matches operation.
success = cache.remove("Hello", 1);

事務支持
 

try (Transaction tx = ignite.transactions().txStart()) {
    Integer hello = cache.get("Hello");

    if (hello == 1)
        cache.put("Hello", 11);

    cache.put("World", 22);

    tx.commit();
}

分佈式鎖
 

// Lock cache key "Hello".
Lock lock = cache.lock("Hello");

lock.lock();

try {
    cache.put("Hello", 11);
    cache.put("World", 22);
}
finally {
    lock.unlock();
} 

部署遠程微服務
        Ignite的服務網格對於在集羣中部署微服務非常有用,Ignite會處理和部署的服務有關的任務的生命週期,並且提供了在應用中調用服務的簡單方式 
下面的例子部署了一個Service,將從客戶端傳過來的信息進行打印 
1.聲明服務接口,擴展Ignite的service接口

import org.apache.ignite.services.Service;

public interface HelloService extends Service {
    // 接受客戶端的信息
    String sayRepeat(String msg);
}

2.實現服務接口,除了實現自己定義的接口外,也可以覆蓋實現Ignite各個生命週期的工作

@Slf4j
public class HelloServiceImpl implements HelloService {

    @Override
    public String sayRepeat(String msg) {
        log.info("I repeat your words : '{}'.", msg);
        return msg;
    }

    @Override
    public void cancel(ServiceContext serviceContext) {
        log.info("{} cancel", serviceContext.name());
    }

    @Override
    public void init(ServiceContext serviceContext) throws Exception {
        log.info("{} init", serviceContext.name());

    }

    @Override
    public void execute(ServiceContext serviceContext) throws Exception {
        log.info("{} execute", serviceContext.name());
    }
}

3.部署服務sayHello,此時服務應用常駐在內存中

@Slf4j
public class IgniteServiceDemo {
    public static void main(String[] args) {
        IgniteServiceDemo demo = new IgniteServiceDemo();
        demo.deploy();
    }

    private void deploy() {
        String serviceName = "sayHello";
        log.info("ready to start service {}", serviceName);
        Ignite ignite = Ignition.start();
        ignite.services().deployClusterSingleton(serviceName, new HelloServiceImpl());
        log.info("service {} have bean started", serviceName);
    }
}

4.在另外一個應用中啓動客戶端,可通過控制檯輸入字符串,調用服務sayHello

@Slf4j
public class IgniteClientDemo {
    public static void main(String[] args) {
        IgniteClientDemo demo = new IgniteClientDemo();
        demo.call();
    }

    private void call() {
        String serviceName = "sayHello";
        try (Ignite ignite = Ignition.start()){
            HelloService helloService = ignite.services().serviceProxy(serviceName, HelloService.class, false);
            log.info("Speaking,please.");
            Scanner in = new Scanner(System.in);
            while(in.hasNext()){
                helloService.sayRepeat(in.next());
            }
        }
    }
}

5.結果

客戶端輸出

[INFO ] 22:41:16.860 [main] com.mumu.IgniteClientDemo - Speaking,please.
Hello!
My name is Ryan

服務端輸出

[INFO ] 22:41:05.481 [srvc-deploy-#43] c.mumu.service.impl.HelloServiceImpl - sayHello init
[INFO ] 22:41:05.481 [service-#46] c.mumu.service.impl.HelloServiceImpl - sayHello execute
[INFO ] 22:41:05.481 [main] com.mumu.IgniteServiceDemo - service sayHello have bean started
[22:41:16] Topology snapshot [ver=2, servers=2, clients=0, CPUs=4, offheap=6.4GB, heap=7.1GB]
[22:41:16]   ^-- Node [id=610DAC7C-B087-405F-8549-8A7FE6012FD4, clusterState=ACTIVE]
[22:41:16] Data Regions Configured:
[22:41:16]   ^-- default [initSize=256.0 MiB, maxSize=3.2 GiB, persistenceEnabled=false]
[INFO ] 22:41:23.086 [svc-#57] c.mumu.service.impl.HelloServiceImpl - I repeat your words : 'Hello!'.
[INFO ] 22:41:35.212 [svc-#59] c.mumu.service.impl.HelloServiceImpl - I repeat your words : 'My name is Ryan'.

後記
上述的例子只是Apache Ignite各種特性及功能中的一部分,作爲一個內存爲核心的框架,它的應用場景極多,既可像Redis一樣作爲緩存,也可像關係型數據庫那樣執行sql語句,同時還能與Spark集成,增強spark的擴展性及速度,還能作爲Hibernate和MyBatis的二級緩存,甚至還可以成爲RPC框架提供微服務。而更可怕的是它具有分佈式特性,可以根據應用規模水平擴展,並且還是運行在內存中,本身速度就佔有很大的優勢。 
由於本人水平有限,加上Ignite屬於比較新的開源項目,在國內外還處於發展階段,很多特性仍在增加變化當中,最權威的介紹可參考官網https://ignite.apache.org/ 
這篇文章對Ignite的特性描述得比較好,值得一讀:https://www.zybuluo.com/liyuj/note/1179662


--------------------- 

轉自: https://blog.csdn.net/vipshop_fin_dev/article/details/82563177 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章