n03 app engineseminar
DESCRIPTION
TRANSCRIPT
GAE/J Scalability
Google Data Store
Google Data Store• Google App Engine 이 사용하는 분산
데이터베이스 시스템• Google File System 을 기반• Peta Byte 급 구조화된 데이터 저장 가능• 구조화된 데이터를 위한 분산 저장 시스템• 범용성 , 확장성 , 고성능 , 고가용성을 위해
개발됨• Non-Relational Database
– Key-Value Store
• JDO/JPA & Native API 지원• 단위 테스트를 위한 Local 버전
서버 ( 최소 100 만대 )
리눅스 OS
GFS(Google File System)
BigTable( 분산스토리지 )
Python
Django
App Application
JAVA
WAS
Bigtable 의 주요 특징
• Big HASHMAP
• NoSQL, Key/Value Store
• No schema
• Entity Group 기반
• Write 비용 : Expensive
• Read 비용 : Cheap
Google Data Store 의 데이터 모델• Google Data Store 의 특징
– Distributed– Persistent– Multidimensional– Sorted
• 디자인 핵심 키워드– Fault-tolerant– Persistent– Scalable– Self-managing
• 동적인 서버 확장 및 조정
MAP
Google Datastore 의 장점
• Excellent read performance.
• Excellent query performance.
• Transparent redundant storage and load balancing.
• Flexible data structure.
• Query indexes.
Datastore 의 제약사항• Write 가 느림
• 검색 가능한 Text 사이즈 : 500 자
• Entity 최대 사이즈 : 1Mb
• 최대 Batch (save/delete) : 500 Entity
• SQL 을 사용할 수 없음
• 데이터베이스의 제약조건을 적용할 수 없음
• Aggregation 함수를 지원하지 않음 (count, avg)
• 쿼리가 최대 조회 레코드 : 1000
• 쿼리에서 Equal 연산 이외의 조건은 1 개만 추가 가능
• 트랜잭션의 단위는 Entity Group 에 한정 됨
GAE/J Data Storage Option
Datastore Architecture
Google Application
Low-Level API
JDO JPA
Bigtable (Master Server)
Tablet Tablet Tablet Tablet
Google File System
……………
FS 0 FS 1 FS 2 FS 3 FS 4 FS n……
Admin Console : Datastore Statistics
Admin Console
Admin Console : Datastore Viewer
Admin Console : Datastore Indexes
Datastore Interface (Native vs Stan-dard)• Low Level API & ORM Standard (JDO & JPA)• Low Level API & ORM Standard 의 기능적인 차이점은
없음• Schemaless 의 특정을 고려할 때 Native API 가 더 적합• ORM Standard 는 Google App Engine 에 애플리케이션
종속성을 추상화– Datanucleus 의 API 종속성을 제거
• Native API 를 사용할 경우 Entity 클래스를 이용하여 데이터 처리– 데이터를 클래스에 맵핑하기 어려움
Native Datastore Interface
Datastore 구성
• 모든 Entity 는 Kind 가 지정• Entity 는 하나이상의 Property 를 포함• 특정 Kind 에 포함된 Entity 는 동일한 Property 를 갖지 않음• Entity 의 Property 는 이름은 동일하지만 데이터 유형이 다를수 있음• Schemaless 의 특성• ID 는 지정하지 않을 경우 자동 할당 됨
Object-Oriented RDBMS Datastore
Class Table Kind
Object Record Entiry
Attribute Column Property
Entity• App Engine Datastore 에서 관리하는 객체는 Entity• Entity 는 하나의 Key 를 포함
– 모든 Entity 중 유일한 구분자• Key 의 구성
– 패스– Parent Entity 의 key– Entity 의 Kind– Entity 에 할당된 이름
• App 이 할당한 값• Datastore 가 지정한 numeric ID
• 지원 데이터 타임 : integers, floating point values, strings, dates, binary data 등 ,
• 각 엔티티는 하나이상의 Property 를 포함– 하나의 Property 에 복수의 데이터 타입 저장 가능– 하나의 Property 에는 복수의 값이 저장 가능– 복수의 값들의 데이터 유형은 다를 수 있음
Datastore 저장 모델• 주요 구성
– Kind : Table– Key : Primary Key– Entity Group : Partitioning– Property : column
Kind Persion
key /Person:iu
Entity Group /Person:iu
Name iu
age 30
Low Level API• com.google.appengine.api.datastore.DatastoreService; • com.google.appengine.api.datastore.DatastoreServiceFactory;• com.google.appengine.api.datastore.Entity; • com.google.appengine.api.datastore.Key;• com.google.appengine.api.datastore.KeyFactory;
Entity 클래스 주요 메서드• com.google.appengine.api.datastore.Entity• 생성자
– public Entity(Key key) – public Entity(String kind) – public Entity(String kind, Key parent) – public Entity(String kind, String keyName) – public Entity(String kind, String keyName, Key parent)
• 주요 Property 관련 메서드– public Map<String,Object> getProperties() – public Object getProperty(String propertyName) – public void removeProperty(String propertyName) – public void setPropertiesFrom(Entity src) – public void setProperty(String propertyName, Object value)
Entity 생성DatastoreService datastore =
DatastoreServiceFactory.getDatastoreService();
Entity player = new Entity("Player");player.setProperty("name", "kim yuna");player.setProperty("age", 20);datastore.put(player);
Entity player2 = new Entity("Player");player2.setProperty("name", "Park Taehwan");player2.setProperty("age", 21);datastore.put(player2);
Entity 생성 - schema-freeDatastoreService datastore =
DatastoreServiceFactory.getDatastoreService();
Entity player = new Entity("Player");player.setProperty("name", ”kim chanho");player.setProperty(”position", “pitcher”);datastore.put(player);
Entity – Root Entity, Entity Group, Ancestor Path Entity employee1 = new Entity("Employee");employee1.setProperty("position", "Boss");employee1.setProperty("age", 60);datastore.put(employee1);
Entity employee2 = new Entity("Employee", employee1.getKey());employee2.setProperty("position", "manager");employee2.setProperty("age", 40);datastore.put(employee2);
Entity employee3 = new Entity("Employee", employee2.getKey());employee3.setProperty("position", "sub-manager");employee3.setProperty("age", 35);datastore.put(employee3)
Entity – Root Entity, Entity Group, Ancestor Path
aglub19hcHBfaWRyDgsSCEVtcGxveWVlGAQM
aglub19hcHBfaWRyHAsSCEVtcGxveWVlGAQMCxIIRW1wbG95ZWUYBQw
aglub19hcHBfaWRyKgsSCEVtcGxveWVlGAQMCxIIRW1wbG95ZWUYBQwLEghFbXBsb3l-lZRgGDA
BossEntity Manager
Entity Sub-man-ager
Entity
Entity – Root Entity, Entity Group, Ancestor Path
aglub19hcHBfaWRy-DgsS-CEVtcGxveWVlGAQM
aglub19hcHBfaWRyHAs-SCEVtcGxveWVlGAQM-CxIIRW1w-bG95ZWUY-BQw
aglub19hcHB-faWRyKgsS-CEVtcGxveWVl-GAQMCxIIR-W1w-bG95ZWUY-BQwLEghFbXBs-b3llZRgGDA
Root Key Instance
Child Key Instance
Child Key Instance
Employee("BOSS")
Employee("BOSS")/Employee("manager")
Employee("BOSS")/Employee("manager")/Em-ployee("sub-manager")
Entity – Root Entity, Entity Group, Ancestor Path
Root Key Instance
Child Key Instance
Child Key Instance
Root Entity
Ancestor Path
Entity Group
Key.getParent()
Key.getParent()
Root & Parent Entity & Key• Root Entity• Parent Entity
Entity emp1= new Entity("Employee");datastore.put(employee);
employeeGroup En-
tity
Entity emp1= new Entity("Employee");datastore.put(employee);
Entity address = new Entity("Address", employee.getKey());datastore.put(address);
employee, addressGroup En-
tity
Employee:8261
Employee:8261
Employee:8261 / Ad-dress:1
Entity emp2= new Entity("Employee");datastore.put(employee);
employeeGroup En-
tity Employee:8262
Entity Group & Transaction• Transaction 의 최대 범위는 Entity Group• Entity Group 은 동일한 Tablet 서버에 위치
Entity emp1= new Entity("Employee");datastore.put(employee);
employeeGroup En-
tity
Entity emp1= new Entity("Employee");datastore.put(employee);
Entity address = new Entity("Address", employee.getKey());datastore.put(address);
employee, addressGroup En-
tity
Employee:8261
Employee:8261
Employee:8261 / Ad-dress:1
Entity emp2= new Entity("Employee");datastore.put(employee);
employeeGroup En-
tity Employee:8262
Entity Group
• 하나의 트랜잭션에서 여러 Entity 를 조회 , 생성 , 수정 , 삭제
할 수 가능하지만 그 범위는 단일 Entity Group 에 국한
• Entity Group 는 부모 엔티티를 갖는 엔티티의 그룹
• 부모 엔티티를 가지 않은 엔티티를 Root 엔티티 라고 함
• Entity Group 은 하나의 Root Entity 와 Child Entity 로 구성
• 하나의 엔티티 그룹은 동일한 분산 네트웍 서버에 저장됨 (Tablet
서버 )
Transaction DatastoreService datastore = DatastoreServiceFactory.getDatastoreService() ;
Transaction txn = datastore.beginTransaction(); try { Key employeeKey = KeyFactory.createKey("Employee", "Joe"); Entity employee = datastore.get(employeeKey); employee.setProperty("vacationDays", 10); datastore.put(employee); txn.commit(); } finally { if (txn.isActive()) { txn.rollback(); } }
Batch Operation
import java.util.Arrays; import java.util.List;
// 코드 생략 Entity employee1 = new Entity("Employee"); Entity employee2 = new Entity("Employee"); Entity employee3 = new Entity("Employee"); // 코드 생략 List<Entity> employees = Arrays.asList (employee1, employee2, employee3); datastore.put(employees);
Entity & Key Class• com.google.appengine.api.datastore.Entity
– public Entity(Key key) – public Entity(java.lang.String kind) – public Entity(java.lang.String kind, Key parent) – public Entity(java.lang.String kind, java.lang.String keyName) – public Entity(java.lang.String kind, java.lang.String keyName, Key
parent)
• com.google.appengine.api.datastore.KeyFactory– public static Key createKey(Key parent, java.lang.String kind, long
id)– public static Key createKey(Key parent, java.lang.String kind,
java.lang.String name) – public static Key createKey(java.lang.String kind, long id) – public static Key createKey(java.lang.String kind, java.lang.String
name)
Entity 조회 , 수정 , 삭제DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();Key rootKey = KeyFactory.createKey("Employee", "BOSS");Entity employee = datastore.get(rootKey);
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();Key rootKey = KeyFactory.createKey("Employee", "BOSS");Datastore.delete(rootKey);
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();Key rootKey = KeyFactory.createKey("Employee", "BOSS");Entity employee = datastore.get(rootKey);employee.setProperty(“age”, 50);datasource.put(employee);
Retrieve
Modify
Remove
Queryimport com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.DatastoreServiceFactory; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.PreparedQuery; import com.google.appengine.api.datastore.Query;
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Query q = new Query(”employee") q.addFilter("lastName", Query.FilterOperator.EQUAL, lastNameParam); q.addFilter("height", Query.FilterOperator.LESS_THAN, maxHeightParam);
PreparedQuery pq = datastore.prepare(q);
for (Entity result : pq.asIterable()) { String firstName = (String) result.getProperty("firstName"); String lastName = (String) result.getProperty("lastName"); Long height = (Long) result.getProperty("height"); System.out.println(lastName + " " + firstName + ", " + height.toString() + " inches tall"); }
Filter Operator• Query.FilterOperator.LESS_THAN• Query.FilterOperator.LESS_THAN_OR_EQUAL• Query.FilterOperator.EQUAL• Query.FilterOperator.GREATER_THAN• Query.FilterOperator.GREATER_THAN_OR_EQUAL• Query.FilterOperator.NOT_EQUAL• Query.FilterOperator.IN
Java Data Object
Portability 확보
JPA 2.0 (Data Nucleus JDO 구현체를 Provider 로 사용 )
JDO 2.3
Data Nucleus 의 JDO Provider
Low Level API
Datastore Interface (JDO vs JPA)• JPA 와 JDO 어노테이션으로 ORM 설정 가능• 두 표준간의 기능적인 차이는 없음• JDO-QL 과 JPA-QL 사용 가능
Google Datastore
Datastore Interface (JDO & JPA)• JPA(Java Persistence API)
– RDBMS 를 저장소로 테이블과 객체 맵핑 표준 스펙• JDO(Java Data Object)
– Non-RDBMS 를 포함하는 저장소를 대상으로 하는 객체 맵핑 표준
App Engine 의 JDO• App Engine Java SDK 는 JDO 구현체를 포함• Datastore 의 영속성 인터페이스로 지원• Version: JDO 2.3• DataNucleus Access Platform 의 구현체 사용
JDO 설치 - jdoconfig.xml & JAR• war/WEB-INF/classes/META-INF/jdoconfig.xml
• 라이브러리 추가– war/WEB-INF/lib/appengine-api.jar
JDO 설치 - Enhance 프로세스 적용• 클래스 컴파일 후 POJO Enhance 프로세스 적용
– java -cp classpath com.google.appengine.tools.enhancer.Enhance class-files
– appengine-java-sdk/lib/appengine-tools-api.jar
JDO 설치 - Enhance 프로세스 적용 in Eclipse
JDO 설치 - Enhance 프로세스 적용 in maven
JDO 설치 - Enhance 프로세스 적용 in Ant<property name="sdk.dir" location="../appengine-java-sdk" />
<import file="${sdk.dir}/config/user/ant-macros.xml" />
<target name="datanucleusenhance" depends="compile" description="Performs JDO enhancement on compiled data classes."> <enhance_war war="war" /></target>
<target name="runserver" depends="datanucleusenhance" description="Starts the development server."> <dev_appserver war="war" port="8888" > <options> <arg value="--jvm_flag=-Xdebug"/> <arg value="--jvm_flag=-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9999"/> </options> </dev_appserver></target>
JDO 설치 - PMF 클래스import javax.jdo.JDOHelper;import javax.jdo.PersistenceManagerFactory;
public final class PMF { private static final PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional");
private PMF() { }
public static PersistenceManagerFactory get() { return pmfInstance; }}
// PersistenceManager pm = PMF.get().getPersistenceManager();
데이터 클래스import com.google.appengine.api.datastore.Key;
import java.util.Date;import javax.jdo.annotations.IdGeneratorStrategy;import javax.jdo.annotations.PersistenceCapable;import javax.jdo.annotations.Persistent;import javax.jdo.annotations.PrimaryKey;
@PersistenceCapablepublic class Employee { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key;
@Persistent private String firstName;
@Persistent private String lastName;
@Persistent private Date hireDate; // 생성자 getter setter 생략}
Object 영속성PersistenceManager pm = PMF.get().getPersistenceManager();
Employee e = new Employee(”Park", ”Chanho", new Date());
try { pm.makePersistent(e);} finally { pm.close();}
Object 영속성 - IIpublic void updateEmployeeTitle(User user, String newTitle) { PersistenceManager pm = PMF.get().getPersistenceManager(); try { Employee e = pm.getObjectById(Employee.class, user.getEmail()); if (titleChangeIsAuthorized(e, newTitle) { e.setTitle(newTitle); } else { throw new UnauthorizedTitleChangeException(e, newTitle); } } finally { pm.close(); }}
pm.deletePersistent(e);
Q&A