Skip to main content

Thao tác cơ bản với Cassandra trong Java

1. Cassandra

Thông thường, các cơ sở dữ liệu loại NoSQL có khả năng tự động phân bố dự liệu giữa các node trong cluster (data distribution) và tạo bản sao dữ liệu (data replication). Cassandra cũng thừa hưởng những tính năng này của NoSQL. Trong bài viết này chúng ta sẽ tìm hiểu về Cassandra và cùng thử giao tiếp với Cassandra bằng Java nhé.

1.1. Những khái niệm chính


Chúng ta cùng bắt đầu bằng một số khái niệm chính của Cassandra:
  • Cluster - tập hợp các nút hoặc Trung tâm dữ liệu được sắp xếp theo cấu trúc vòng. Tên phải được gán cho mỗi cụm.
  • Keyspace - Nếu bạn đã biết đến một cơ sở dữ liệu quan hệ, thì Schema là keyspace tương ứng trong Cassandra. Keyspace là container ngoài cùng cho dữ liệu trong Cassandra. Các thuộc tính chính trên mỗi keyspace là Replication Factor, Replica Placement Strategy và Column Families
  • Column Family - Column Families trong Cassandra giống như các bảng trong cơ sở dữ liệu quan hệ. Mỗi Column Family chứa một tập hợp các hàng được đại diện bởi một Map <RowKey, SortedMap <ColumnKey, ColumnValue >> . Khóa cung cấp khả năng truy cập dữ liệu liên quan cùng nhau
  • Column - Cột trong Cassandra là cấu trúc dữ liệu gồm column name, value và timestamp. Các cột và số cột trong mỗi hàng có thể thay đổi ngược lại với cơ sở dữ liệu quan hệ nơi dữ liệu được cấu trúc tốt

2. Thao tác cơ bản với Cassandra trong Java

2.1. Maven Dependency

 Chúng ta cần định nghĩa Cassandra dependency trong file pom.xml, version mới nhất các bạn có thể vào trang chủ của Cassandra để xem:
1
2
3
4
5
<dependency>
    <groupId>com.datastax.cassandra</groupId>
    <artifactId>cassandra-driver-core</artifactId>
    <version>3.1.0</version>
</dependency>


Để test code với một embedded database server , các bạn thêm cassandra-unit vào nữa nhé:
1
2
3
4
5
<dependency>
    <groupId>org.cassandraunit</groupId>
    <artifactId>cassandra-unit</artifactId>
    <version>3.0.0.1</version>
</dependency>





2.2. Kết nối Cassandra


Để kết nối với Cassandra từ Java, chúng ta cần xây dựng một đối tượng Cluster .


Địa chỉ của một nút (node) cần được cung cấp dưới dạng contact point. Nếu chúng ta không cung cấp port, port mặc định (9042) sẽ được sử dụng.
Hãy xem cách cài đặt dưới đây:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class CassandraConnector {
    private Cluster cluster;
    private Session session;
    public void connect(String node, Integer port) {
        Builder b = Cluster.builder().addContactPoint(node);
        if (port != null) {
            b.withPort(port);
        }
        cluster = b.build();
        session = cluster.connect();
    }
    public Session getSession() {
        return this.session;
    }
    public void close() {
        session.close();
        cluster.close();
    }
}

2.3. Tạo một Keyspace

Thử tạo Keyspace với tên "library":
1
2
3
4
5
6
7
8
9
10
11
12
public void createKeyspace(
  String keyspaceName, String replicationStrategy, int replicationFactor) {
  StringBuilder sb =
    new StringBuilder("CREATE KEYSPACE IF NOT EXISTS ")
      .append(keyspaceName).append(" WITH replication = {")
      .append("'class':'").append(replicationStrategy)
      .append("','replication_factor':").append(replicationFactor)
      .append("};");
         
    String query = sb.toString();
    session.execute(query);
}

Ngoài keyspaceName chún ta cần define ra 2 parameters nữa là replicationFactor và replicationStrategy. Các tham số này xác định số lượng bản sao
và cách thức các bản sao sẽ được phân phối trên toàn bộ vòng tương ứng.

Lúc này chúng ta có thể kiểm tra keyspace đã được tạo thành công:
1
2
3
4
5
6
7
8
9
10
private KeyspaceRepository schemaRepository;
private Session session;
@Before
public void connect() {
    CassandraConnector client = new CassandraConnector();
    client.connect("127.0.0.1", 9142);
    this.session = client.getSession();
    schemaRepository = new KeyspaceRepository(session);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void whenCreatingAKeyspace_thenCreated() {
    String keyspaceName = "library";
    schemaRepository.createKeyspace(keyspaceName, "SimpleStrategy", 1);
    ResultSet result =
      session.execute("SELECT * FROM system_schema.keyspaces;");
    List<String> matchedKeyspaces = result.all()
      .stream()
      .filter(r -> r.getString(0).equals(keyspaceName.toLowerCase()))
      .map(r -> r.getString(0))
      .collect(Collectors.toList());
    assertEquals(matchedKeyspaces.size(), 1);
    assertTrue(matchedKeyspaces.get(0).equals(keyspaceName.toLowerCase()));
}

2.4. Tạo Column Family

Bây giờ ta sẽ thêm Column Family "books" và keyspace vừa tạo ở trên:
1
2
3
4
5
6
7
8
9
10
11
12
13
private static final String TABLE_NAME = "books";
private Session session;
public void createTable() {
    StringBuilder sb = new StringBuilder("CREATE TABLE IF NOT EXISTS ")
      .append(TABLE_NAME).append("(")
      .append("id uuid PRIMARY KEY, ")
      .append("title text,")
      .append("subject text);");
    String query = sb.toString();
    session.execute(query);
}

Code để test Column Family vừa mới tạo như sau:
1
2
3
4
5
6
7
8
9
10
private BookRepository bookRepository;
private Session session;
@Before
public void connect() {
    CassandraConnector client = new CassandraConnector();
    client.connect("127.0.0.1", 9142);
    this.session = client.getSession();
    bookRepository = new BookRepository(session);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void whenCreatingATable_thenCreatedCorrectly() {
    bookRepository.createTable();
    ResultSet result = session.execute(
      "SELECT * FROM " + KEYSPACE_NAME + ".books;");
    List<String> columnNames =
      result.getColumnDefinitions().asList().stream()
      .map(cl -> cl.getName())
      .collect(Collectors.toList());
         
    assertEquals(columnNames.size(), 3);
    assertTrue(columnNames.contains("id"));
    assertTrue(columnNames.contains("title"));
    assertTrue(columnNames.contains("subject"));
}


2.5. Insert dữ liệu vào Column Family
Bảng books vừa tạo ở trên, bây giờ ta sẽ insert dữ liệu vào bảng đó:

1
2
3
4
5
6
7
8
9
public void insertbookByTitle(Book book) {
    StringBuilder sb = new StringBuilder("INSERT INTO ")
      .append(TABLE_NAME_BY_TITLE).append("(id, title) ")
      .append("VALUES (").append(book.getId())
      .append(", '").append(book.getTitle()).append("');");
    String query = sb.toString();
    session.execute(query);
}
Thử test dữ liệu chúng ta vừa insert vào:
1
2
3
4
5
6
7
8
9
10
11
@Test
public void whenAddingANewBook_thenBookExists() {
    bookRepository.createTableBooksByTitle();
    String title = "Effective Java";
    Book book = new Book(UUIDs.timeBased(), title, "Programming");
    bookRepository.insertbookByTitle(book);
         
    Book savedBook = bookRepository.selectByTitle(title);
    assertEquals(book.getTitle(), savedBook.getTitle());
}
Tạo 1 bảng khác có tên booksByTitle:
1
2
3
4
5
6
7
8
9
10
public void createTableBooksByTitle() {
    StringBuilder sb = new StringBuilder("CREATE TABLE IF NOT EXISTS ")
      .append("booksByTitle").append("(")
      .append("id uuid, ")
      .append("title text,")
      .append("PRIMARY KEY (title, id));");
    String query = sb.toString();
    session.execute(query);
}
Trong Cassandra, một trong những phương pháp hay nhất là one-table-per-query.
Có nghĩa là mỗi query khác nhau chúng ra sẽ cần 1 bảng khác nhau.
Ở ví dụ này chúng ta đã select book by title. Để đáp ứng query selectByTitle, ta đã tạo ra 1 bảng mới với PRIMARY KEY là title và id. 
Như vậy bằng cách này có thể sẽ có rất nhiều dữ liệu trùng lặp, tuy nhiên đó không phải là nhược điểm của Cassandra. Ngược lại, cách này giúp tối ưu hóa việc truy xuất dữ liệu.

2.7. Xóa Column Family

Đoạn code ở dưới sẽ chỉ cho bạn cách xóa 1 table:
1
2
3
4
5
6
7
public void deleteTable() {
    StringBuilder sb =
      new StringBuilder("DROP TABLE IF EXISTS ").append(TABLE_NAME);
    String query = sb.toString();
    session.execute(query);
}
Select 1 bảng không tồn tại trong keyspace sẽ trả về nvalidQueryException:
1
2
3
4
5
6
7
@Test(expected = InvalidQueryException.class)
public void whenDeletingATable_thenUnconfiguredTable() {
    bookRepository.createTable();
    bookRepository.deleteTable("books");
        
    session.execute("SELECT * FROM " + KEYSPACE_NAME + ".books;");
}

2.8. Xóa Keypsace

1
2
3
4
5
6
7
public void deleteKeyspace(String keyspaceName) {
    StringBuilder sb =
      new StringBuilder("DROP KEYSPACE ").append(keyspaceName);
    String query = sb.toString();
    session.execute(query);
}
Xóa 1 keypsace và test xem keyspace đã được xóa hay chưa:
1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void whenDeletingAKeyspace_thenDoesNotExist() {
    String keyspaceName = "library";
    schemaRepository.deleteKeyspace(keyspaceName);
    ResultSet result =
      session.execute("SELECT * FROM system_schema.keyspaces;");
    boolean isKeyspaceCreated = result.all().stream()
      .anyMatch(r -> r.getString(0).equals(keyspaceName.toLowerCase()));
         
    assertFalse(isKeyspaceCreated);
}

3. Kết luận

Hướng dẫn này trình bày các bước cơ bản để kết nối và sử dụng cơ sở dữ liệu Cassandra với Java. Một số khái niệm chính của cơ sở dữ liệu này cũng đã được đưa ra giúp các bạn dễ hình dung hơn khi mới bắt đầu với Cassandra.

Comments

Popular posts from this blog

Ứng dụng giải thuật MiniMax trong trò chơi cờ tướng - Tìm hiểu về trí tuệ nhân tạo (Phần 2)

Chắc hẳn mọi người đều biết về trò chơi thú vị như cờ tướng. Tiếp theo loạt bài về trí tuệ nhân tạo, bài viết này mình sẽ nói về cụ thể giải thuật Minimax ứng dụng trong trò chơi trí tuệ cờ tướng như thế nào. OK! Let's go. 1. Ý tưởng Cờ tướng là trò chơi đối kháng, trong đó hai người luôn phiên nhau đi nước đi của mình. Trạng thái bắt đầu là trạng thái khởi tạo bàn cờ, sau mỗi nước đi của một bên, trạng thái bàn cờ sẽ được thay đổi thành một trạng thái mới hiện hành. Cờ tướng có luật của nó, và trò chơi sẽ kết thúc khi một người có được trạng thái phản ánh sự thắng cuộc hoặc hai người rơi vào trạng thái hòa cờ. Ta tìm cách phân tích xem từ một trạng thái nào đó sẽ dẫn đến đấu thủ nào sẽ thắng với điều kiện cả hai có trình độ như nhau. Giải thuật Minimax sẽ được áp dụng vào trong trò chơi cờ tướng. Hai đấu thủ trong trò chơi sẽ được gọi là MIN và MAX và hai đấu

Sử dụng Jedis làm việc với Redis trong Java

Bài viết này mình sẽ   giới thiệu về Jedis , một thư viện client Java cho  Redis . 1. Tại sao lại là Jedis? Redis liệt kê các thư viện client nổi tiếng nhất trên  trang web chính thức  của họ  .  Có nhiều lựa chọn thay thế cho Jedis, nhưng chỉ có hai lựa chọn khác xứng đáng để đề xuất đó là  lettuce  và  Redisson . Hai clients này có một số tính năng độc đáo như an toàn luồng, xử lý kết nối lại trong suốt và API không đồng bộ, tất cả các tính năng mà Jedis thiếu. Tuy nhiên, Jedis nhỏ và nhanh hơn đáng kể so với hai loại kia.  Bên cạnh đó, nó là thư viện client được lựa chọn của các nhà phát triển Spring Framework, và nó có cộng đồng lớn nhất trong cả ba. 2. Maven Dependencies Hãy bắt đầu bằng cách khai báo dependency trong file  pom.xml  : 1 2 3 4 5 < dependency >      < groupId >redis.clients</ groupId >      < artifactId >jedis</ artifactId >      < version >2.8.1</ version > </ dependency >

Sử dụng Jenkins để Build Docker Images

Khởi chạy Jenkins Khởi chạy Jenkins như một Docker Container với lệnh sau: docker run -d -u root --name jenkins \ -p 8080:8080 -p 50000:50000 \ -v /root/jenkins_2112:/var/jenkins_home \ jenkins/jenkins:2.112-alpine Load Dashboard Tên người dùng  admin có mật khẩu mặc định là  344827fbdbfb40d5aac067c7a07b9230 Trên hệ thống của bạn, bạn có thể tìm mật khẩu bằng docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword Có thể mất vài giây để Jenkins hoàn thành việc bắt đầu và có sẵn.  Trong các bước tiếp theo, bạn sẽ sử dụng trang Dashboard để định cấu hình các plugin và bắt đầu tạo Image Docker. Cấu hình Plugin Docker Bước đầu tiên là cấu hình  plugin Docker  .  Plugin này dựa trên plugin Jenkins Cloud.  Khi build Docker Image, nó sẽ tạo ra một "Cloud Agent" thông qua plugin.  Tác nhân sẽ là Docker Container được cấu hình để giao tiếp với Docker Daemon Job build của Jenkins sẽ sử dụng vùng chứa nà