Czemu opłacała mi się nauka DynamoDB od Amazon AWS

W tym artykule pokażę Ci jak prosta i przyjemna jest praca z bazą DynamoDB, dodatkowo opowiem Ci po kolei o:

  1. Co to są bazy nosql – historia powstania, różnice wobec baz relacyjnych
  2. Ranking popularności baz nosql
  3. Zarys historyczny powstania bazy dynamo db i kto teraz używa
  4. Co zbudujemy w naszym przykładzie
  5. Budujemy infrastrukturę w AmazonAWS
  6. Omówienie kodu
  7. Podsumowanie

Z relacyjnymi bazami danych styczność miał chyba każdy i każdy jest mniej lub bardziej świadomy ich ograniczeń,  a skoro jest to blog m.in. o chmurze publicznej to musze wspomnieć o ich ułomności jaką jest skalowalność wszerz (vertical scalability).

Wraz z popularyzacją serwisów WWW dla milionów użytkowników, serwisów społecznościowych,  terabajtami danych jakie zostają po user’ach w internecie, popularyzacją zagadnień BigData, Facebook, Google czy Amazon zaczęły popularyzować rozwiązania oparte o bazy NoSql.

No dobrze, więc co to są te bazy NoSql i dlaczego od początku 20 wieku zaczęły odbierać bazom SQL część rynku? Już pewnie czujesz, że wspomnę coś o skalowalności 🙂 Tak, dobrze się domyślasz,  świetnie się skalują w serwerach połączonych w klastry, a do tego jeszcze prosto się je projektuje – no może nie bajecznie prosto, ale łatwiej niż bazy relacyjne.

Bazy relacyjne mają wciąż silną pozycje na rynku, ponieważ w wielu przypadkach ich ograniczenia są ich atutami np.

  • współbieżność – przy określeniu odpowiedniego poziomu transakcji mamy pewność, że wszystkie operacje wykonane przez użytkowników będą spójne
  • trwałe składowanie danych – po utracie zasilania i przywróceniu bazy do działania ponownie nasze dane nadal tam będą
  • rollback – dzięki transakcją mamy mechanizm, który po wykryciu błędu pozwoli wycofać zmiany i przywrócić stan sprzed transakcji
  • transakcje – pozwalają na to aby wiele aplikacji (klientów) mogło jednocześnie pracować na tych samych, a i tak spójność danych zostanie zachowana

A jaka jest ich słabość? W jakich aspektach bazy NoSQL mają przewagę?

  • ACID przeszkadza bazą relacyjnym pracować w klastrze, bazy NoSQL świetnie się skalują
  • Model danych jest mało elastyczny, ciężko nanosi się zmiany na tabele jeżeli te są już wypełnione danymi, ciężko rozszerza się je o nowe kolumny

Projektując nasze rozwiązanie musimy zawsze wziąć pod uwagę powyższe aspekty i podjąć dobrą decyzję.


Kiedyś gdy dopiero zaczynałem interesować się bazami NoSql i tym w jaki sposób przechowuję dane, myślałem, że nie ma tu podziału. Wrzucamy zawsze plik JSON i niech magia w środku dzieje się sama. Na szczęście ciekawość popchnęła mnie żeby do sprawdzić:) Ok, więc możemy wyróżnić cztery typy baz NoSql:

  1. Document-Oriented databases – jest to główna grupa baz typu NoSql, najważniejszym pojęciem i bytem w takich bazach jest dokument. Wszystko co zapisujemy jest dokumentem, a za format zapisywanych danych może odpowiadać XML, JSON, YAML czy BSON (binarny format danych do zapisywania np plików PDF). Jak do tej pory najpopularniejszą bazą tego typu z którą miałem okazję pracować to baza MongoDB, dzisiaj zaczynamy naukę DynamoDB (DynamoDB wspiera również model zapisu klucz-wartość)
  2. Key-Value databases  – jest to najprostszy model bazy NoSql. Każdy element w bazie zapisywany jest z jako para klucz (key) – pozwala na identyfikację elementu) – oraz sam element jako wartość (value). Jako przykład możemy wymienić tu bazę DynamoDB, ActivePivot, Redis czy Riak. Przypadkiem biznesowym, z którym miałem okazję spotkać się było użycie bazy ActivePivot jako cache’a pomiędzy aplikacja enterprise, a bazą NoSql – rozwiązanie takie w znaczący sposób przyspieszyło działanie aplikacji, która była nastawiona na serwowanie dużej ilości danych do środowiska rozporszonego.
  3. Graph databases – najpopularniejszym przypadkiem użycia tego typu baz jest modelowanie sieci połączeń np. relacji kontaktów w serwisach społecznościowych. Przykłady to Neo4j, HyperGraphDB.
  4. Column Store databases – w typ przypadku zastosowano podejście, w którym dane są agregowane w postaci kolumn a nie wierszy – takie podejście pozwala na optymalizację zapytań na dużych zbiorach danych. Najpopularniejsze rozwiazanie dostarcza tutaj Cassandra i HBase.

Amazon DynamoDB (link do strony) jest szybką i elastyczną bazą NoSql dostarczaną jako jeden z serwisów Amazon AWS. Jest dedykowana dla aplikacji, które potrzebują stałego, na poziomie mikrosekund dostępu do danych. Jest w pełni zarządzana w chmurze Amazon AWS i wspiera dwa modele zapisu danych – Document-Oriented i Klucz-Wartość.

Jeżeli chodzi o zarys historyczny powstania to na Wikipedii możemy przeczytać, że jako pierwszy raz została udostępniona przez Amazona w sierpniu 2013 roku w wersji to uruchomienia na lokalnych środowiskach deweloperskich – jak widać jest to świeże rozwiązanie. Dalej to co mnie zainteresowało w informacjach o bazie to, że przy projektowaniu rozwiązania zwrócono uwagę to aby baza łatwo integrowała się rozwiązaniami od innych dostawców – możemy przeczytać o wsparciu dla Hadoop Elastic MapReduce.

Kto używa DynamoDB? Na stronie Amazona możemy zobaczyć loga firm, które pewnie kojarzysz 🙂

Projekt

To co zbudujemy i to co postaram Wam się pokazać to prosta aplikacja oparta (jak zwykle:)) o Spring Boot’a dzięki, któremu wystawimy REST’owy serwis wraz z operacjami zapewniającymi CRUD’a. Pozwoli nam to na sprawdzenie jak w praktyce wyglada praca z bazą DynamoDB – mam na myśli tutaj, że wykonamy na bazie operacje zapisywania, edytowania, pobierania i usuwania. Do integracji z bazą DynamoDB wykorzystamy bibliotekę Spring Data JPA oraz dostarczoną od Amazona AWS Java SDK DynamoDB – oczywiście dodane w Naszym projekcie jako zależności Maven’a. Aplikację zainstalujemy w chmurze w serwisie EC2 przy pomocy Elastic BeanStalk. Nasza baza MongoDB zostanie zainstalowana w tej samej sieci publicznej co serwis EC2 – oznacza, to że nie będziemy skupiać się na budowania zaawansowanej architektury sieciowej.

Kod do pobrania dostępny jest tutaj.

Konfiguracja DynamoDB w AWS

Ok, zacznijmy konfigurować DynamoDB, to będzie długi, żmudny i trudny proces. Czeka przed nami przygotowanie instancji EC2, konfiguracja podsieci w której uruchomimy bazę, trzeba będzie nadać uprawnienia, skonfigurować grupy.. uff trochę przed nami co?

Na początek musimy dodać użytkownika i wygenerować dla niego accessKey i secretKey, w tym celu w module IAM wchodzimy w zakładkę Users i klikamy Add user.

Chcemy nasz użytkownik miał jedynie dostęp przez AWS API dlatego zaznaczamy opcję Programmatic access.

Dodajemy uprawnienia do łączenia się z bazą DynamoDB.

A teraz bardzo ważne, w ostatnim kroku skopiujcie Access key ID i Secret Key ID. Tak proszę żebyś skopiował sobie te wartości na boku, ponieważ po pierwsze będą nam potrzebne aby skonfigurować połączenie z bazą, a po drugie.. już więcej nie zobaczysz na oczy wygenerowanego przez AWS Secret Key ID, AWS nie pozwoli Ci już więcej go podejrzeć.

Mamy to!

Teraz potrzebujemy stworzyć tabelę, w której będziemy zapisywać nasze dokumenty, powiedzmy, że mamy firmę szkoleniową i w naszej bazie NoSql chcemy zapisywać informacje o dostępnych treningach. Pamiętasz jak wspominałem, że proces konfiguracji infrastruktury pod bazę DynamoDB jest długi i trudny? W konsoli AWS wchodzimy kolejno w Services -> Database -> DynamoDB

Podajemy nazwę naszej tabeli i nazwę klucza, który identyfikuje pojedynczy wpis.

Klikamy Create i pyk! Wszystko gotowe. Tak, na prawdę to wszystko, wcale nie musimy konfigurować tego o czym wspominałem wcześniej. Mega co?! Też byłem zaskoczony. To ciekawe, pamiętasz ile pracy trzeba było kiedyś wykonać aby postawić bazę SQL, jeszcze dostępna publicznie? A teraz w zasadzie w 3 kliknięciach mamy bazę gotową do pracy, gotową żeby załadować ją naszymi danymi.

 

Omówienie kodu

Jak już wspominałem, aplikacja powstanie w oparciu o mój ulubiony framework Spring Boot, czemu ulubiony? Ponieważ mnóstwo konfiguracji, która i tak zawsze wygląda tak samo, Spring Boot zrobi za nas i pozwoli skupić się na kodowaniu.

Na początek będą potrzebne nam trzy biblioteki, które dodany jako zależności Maven’a w pliku pom.xml – dwie pochodzą z Community Spring’a a jedna z SDK AWS

<!-- DynamoDB dependencies-->
<dependency>
  <groupId>org.springframework.data</groupId>
  <artifactId>spring-data-releasetrain</artifactId>
  <version>Hopper-SR10</version>
</dependency>
<dependency>
  <groupId>com.amazonaws</groupId>
  <artifactId>aws-java-sdk-dynamodb</artifactId>
  <version>1.11.34</version>
</dependency>
<dependency>
  <groupId>com.github.derjust</groupId>
  <artifactId>spring-data-dynamodb</artifactId>
  <version>4.3.1</version>
</dependency>

Następnie, skonfigurujmy plik properties. Będziesz musiał wprowadzić wygenerowane wcześniej Access key ID i Secret Key ID

amazon.aws.accesskey=***
amazon.aws.secretkey=***
amazon.aws.region=eu-west-1

Za spięcie wszystkiego w całość odpowiada klasa DynamoDBConfig. Dzięki adnotacjom @Value, gdy spring zbuduje swój kontekst wtedy wstrzyknie konkretne wartości z ustawieniami i konfiguracja będzie dostępna dla pozostałej części aplikacji. Proszę zwróć uwagę na region który został też ustawiony. Gdy pierwszy raz próbowałem połączyć się bazą w chmurze dostawałem błąd o braku dostępu do resource AWS, niestety żadne tutoriale na które natknąłem się w sieci nie uwzględniały tego kroku, ale przypomniałem sobie o podobnym problemie gdy pracowałem nad flow dla CodeDeploy. Dlatego w tym wypadku jeżeli ustawimy parametr amazon.aws.region to nasza wartość zostanie wczytana i aplikacja połączy się z Twoim regionem. Jeżeli nie ustawisz tej wartości to połączenie nastąpi default’owo z regionem Irlandia.

@Configuration
@EnableDynamoDBRepositories(basePackages = "com.chmurowisko.repository")
public class DynamoDBConfig {

    @Value("${amazon.aws.region:''}")
    private String amazonAWSRegion;

    @Value("${amazon.aws.accesskey}")
    private String amazonAWSAccessKey;

    @Value("${amazon.aws.secretkey}")
    private String amazonAWSSecretKey;

    @Bean
    public AmazonDynamoDB amazonDynamoDB() {
        AmazonDynamoDB amazonDynamoDB
                = new AmazonDynamoDBClient(amazonAWSCredentials());

        if (!StringUtils.isEmpty(amazonAWSRegion)) {
            Region region = RegionUtils.getRegion(amazonAWSRegion);
            amazonDynamoDB.setRegion(region);
        } else {
            amazonDynamoDB.setRegion(Region.getRegion(Regions.EU_WEST_1));
        }

        return amazonDynamoDB;
    }

    @Bean
    public AWSCredentials amazonAWSCredentials() {
        return new BasicAWSCredentials(
                amazonAWSAccessKey, amazonAWSSecretKey);
    }
}

Nasza encja, która będzie reprezentować dokument treningu wygląda jak poniżej, dla naszych testów zapiszemy takie wartości jak nazwę, poziom, autora, datę dodania i modyfikacji. W bazach relacyjnych za identyfikację pojedynczego rekordu odpowiada klucz główny, prawda? Zazwyczaj generowany przy użyciu sekwencji bazodanowej lub mechanizmów JPA. W przypadku DynamoDB i SDK od AWS, pole które zidentyfikuje nasz obiekt przy użyciu unikalnego klucza oznaczamy adnotacją @DynamoDBHashKey, dodatkowo aby zadbać o automatyczny mechanizm generowania tego klucza, używamy drugiej adnotacji @DynamoDBAutoGeneratedKey – prawda, że proste? Pamiętaj jeszcze tylko, że kolumnę trainingId oznaczyliśmy jako klucz w momencie definiowania tabelki w konsoli AWS.

@DynamoDBTable(tableName = "Training")
public class Training {


    @DynamoDBHashKey
    @DynamoDBAutoGeneratedKey
    private String trainingId;

    @DynamoDBAttribute
    private String name;

    @DynamoDBAttribute
    private Long level;

    @DynamoDBAttribute
    private String author;

    @DynamoDBAttribute
    private Date createDate;

    @DynamoDBAttribute
    private Date updateDate;

    public String getTrainingId() {
        return trainingId;
    }

    public void setTrainingId(String trainingId) {
        this.trainingId = trainingId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getLevel() {
        return level;
    }

    public void setLevel(Long level) {
        this.level = level;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public Date getUpdateDate() {
        return updateDate;
    }

    public void setUpdateDate(Date updateDate) {
        this.updateDate = updateDate;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }
}

Warstwa persystencji – czyli ten fragment kodu, który odpowiada za tłumaczenie kodu napisanego w JAVA’ie na język zrozumiały przez DynamoDB. WOW, pusta klasa i to wszystko? Dokladnie, dzięki dziedziczeniu z CrudRepository i adnotacji @EnableScan mamy zapewnionego całego CRUDa dla naszych treningów.

@EnableScan
public interface TrainingRepository extends CrudRepository<Training, String> {

}

Warstwa WEB, która pozwoli nam na podstawowe operacje czyli wylistowanie wszystkich dostępnych treningów, dodanie nowego, edycję istniejącego oraz usunięcie wygląda następująco

@RestController
@RequestMapping("/training")
public class TrainingResource {

    @Autowired
    private TrainingRepository trainingRepository;

    @RequestMapping(path = "/list", method = RequestMethod.GET)
    public ResponseEntity<Iterable<Training>> list() {
        return new ResponseEntity<>(
                trainingRepository.findAll(), HttpStatus.OK);
    }

    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<String> save(@RequestBody Training training) {
        Date now = new Date();
        training.setCreateDate(now);
        training.setUpdateDate(now);
        trainingRepository.save(training);
        return new ResponseEntity<>("SAVED", HttpStatus.OK);
    }

    @RequestMapping(method = RequestMethod.PUT)
    public ResponseEntity<String> update(@RequestBody Training training) {
        Assert.isTrue(
                trainingRepository.exists(training.getTrainingId()), "Can't update, entity doesn't exist");
        training.setUpdateDate(new Date());
        trainingRepository.save(training);
        return new ResponseEntity<>("UPDATED", HttpStatus.OK);
    }

    @RequestMapping(method = RequestMethod.DELETE)
    public ResponseEntity<String> delete(@RequestBody Training training) {
        trainingRepository.delete(training);
        return new ResponseEntity<>("DELETED", HttpStatus.OK);
    }

}

Testowanie

Aplikacja opiera się o Spring Boot dlatego jeżeli mamy zainstalowanego Maven’a to w bardzo łatwy sposób możemy ją zbudować i uruchomić lokalnie. Wystarczy, że ze środka katalogu projektu użyjemy komendy:

./mvnw spring-boot:run

Osoboście do testowania REST’owych serwisów używam darmowego oprogramowania Postman

 

Dobrze,  na początek spróbujmy pobrać listę dostępnych treningów przez wywołanie metody

HTTP GET na http://localhost:8080/training/list

OUT

[]

Dodajmy dwa treningi, aby to zrobić wywołujemy metodę POST i przesyłamy dwa JSON’y z informacjami o treningach

HTTP POST na http://localhost:8080/training

BODY

{
  "name": "Chmurowisko Specialista 1",
  "level": 1,
  "author": "http://chmurowisko.pl/"
}
{
  "name": "Chmurowisko Specialista 2",
  "level": 2,
  "author": "http://chmurowisko.pl/"
}

Pobieramy znowu listę dostępnych treningów i tym razem dostajemy wynik

HTTP GET na http://localhost:8080/training/list

OUT

[
  {
    "trainingId": "25d55dd1-91cc-4344-8ba6-c9e1ad20ecea",
    "name": "Chmurowisko Specialista 1",
    "level": 1,
    "author": "http://chmurowisko.pl/",
    "createDate": 1496120448472,
    "updateDate": 1496120448472
  },
  {
    "trainingId": "4f14cc6b-b77b-4073-9d4d-8321163e5138",
    "name": "Chmurowisko Specialista 2",
    "level": 2,
    "author": "http://chmurowisko.pl/",
    "createDate": 1496120484693,
    "updateDate": 1496120484693
  }
]

Edytujemy drugi trening, chcemy zmienić nazwę i zwiększyć poziom na 3. W tym celu użyjemy metody PUT z trainingId podanym w body

HTTP PUT na http://localhost:8080/training

BODY

{
  "trainingId" : "4f14cc6b-b77b-4073-9d4d-8321163e5138",
  "name": "Chmurowisko Specialista 2 rozszczerzony",
  "level": 3,
  "author": "http://chmurowisko.pl/"
}

Sprawdźmy czy nasze zmiany są widoczne

HTTP GET na http://localhost:8080/training/list

OUT

[
  {
    "trainingId": "25d55dd1-91cc-4344-8ba6-c9e1ad20ecea",
    "name": "Chmurowisko Specialista 1",
    "level": 1,
    "author": "http://chmurowisko.pl/",
    "createDate": 1496120448472,
    "updateDate": 1496120448472
  },
  {
    "trainingId": "4f14cc6b-b77b-4073-9d4d-8321163e5138",
    "name": "Chmurowisko Specialista 2 rozszczerzony",
    "level": 3,
    "author": "http://chmurowisko.pl/",
    "createDate": null,
    "updateDate": 1496120764319
  }
]

Teraz usuniemy pierwdzy trening, w tym celu wykorzystamy metode DELETE

HTTP DELETE na http://localhost:8080/training

BODY

{
  "trainingId" : "25d55dd1-91cc-4344-8ba6-c9e1ad20ecea"
}

Czy na naszej liście pozostanie już tylko drugi trening? Oczywiście, że tak 🙂

HTTP GET na http://localhost:8080/training/list

OUT

[
  {
    "trainingId": "4f14cc6b-b77b-4073-9d4d-8321163e5138",
    "name": "Chmurowisko Specialista 2 rozszczerzony",
    "level": 3,
    "author": "http://chmurowisko.pl/",
    "createDate": null,
    "updateDate": 1496120764319
  }
]

 

Podsumowanie

Jak widać praca z bazą DynamoDB jest prosta i przyjemna i ćwiczenie to nazwałbym osobiście wstępem do świata NoSQL. Oczywiście pokazałem najprostszy przypadek gdzie pracowaliśmy z kilkoma rekordami ale kusi mnie aby w jednym z następnych postów zrobić testy obciążeniowe na dużej ilości danych. Nie pokazałem tego w poście ale AWS SDK umożliwia nam pisanie bardziej skomplikowanych zapytań czy modelowanie zależności między dokumentami (relacji).

Dlaczego opłacała mi się nauka i praca z bazą DynamoDB? Ponieważ uważam, że zawsze warto uczyć się nowych rzeczy i nawet jeżeli na dzień dzisiejszy nie kojarzę ofert pracy z wymaganiami co do DynamoDB to wcale nie jest wykluczone, że najbliższej przyszłości może się to zmienić. Na pewno był to przyjemny wstęp do pracy z bazami NoSQL.

Mam nadzieję, że podobało się również Tobie 🙂

2 comments on “Czemu opłacała mi się nauka DynamoDB od Amazon AWS

Leave a Reply

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *