파이썬 기본 문법을 정리해보았습니다.
  기존에 개발을 해 보신 분들이, 예제를 통해 대략적으로 파이썬 기본 문법을 파악하기 위한 용도입니다.
  따라서, 긴 설명 없이 항목 별 예제를 중심으로 정리하였으며, 추가적인 설명이 필요한 경우에만 간략하게 정리하였습니다.

  각 '주제'를 클릭하면 관련 내용이 명시된 파이썬 자습서 홈페이지로 이동합니다.

 


 

파이썬 인터프리터

  - 대화형 모드로 동작

 

 


문자열 연산 : 불변

# 문자열 연산
>>> word = 'Python'
>>> word[0]
'P'
>>> word[-2]
'o'
>>> word[:2]
'Py'
>>> word[2:5]
'tho'
>>> 3 * word[0] + word[1:4] + '~~~' + word[4:]
'PPPyth~~~on'
>>> len(word)
6

 

 


if 문

>>> x = int(input("Please enter an integer: "))
Please enter an integer: 42
>>> if x < 0:
...         x = 0
...         print('Negative changed to zero')
...     elif x == 0:
...         print('Zero')
...     elif x == 1:
...         print('Single')
...     else:
...         print('More')
...
More

 

for 문

>>> words = ['cat', 'window', 'defenestrate']
>>> for w in words:
...         print(w, len(w))
...
cat 3
window 6
defenestrate 12
>>> for n in range(2, 10):
...         for x in range(2, n):
...             if n % x == 0:
...                 print(n, 'equals', x, '*', n//x)
...                 break
...         else:
...             print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3

 

 


리스트 연산

  - Compound 데이터 타입 중 대표적
  - 가변인 시퀀스형
  - 대괄호나 list 함수를 이용하여 생성

>>> squares = [1, 4, 9, 16]
>>> squares 
[1, 4, 9, 16]
>>> squares[2]
9
>>> squares + [36, 49]
[1, 4, 9, 16, 36, 49]
>>> squares.append(50)
>>> len(squares)
5
>>> x = [squares, squares]
>>> x[0]
[1, 4, 9, 16, 50]
>>> x[0][1]
4
>>> lst = list('abcde')
>>> lst
['a', 'b', 'c', 'd', 'e']

 

 

리스트 메소드

append(x) 리스트의 끝에 항목 추가. a[len(a):] = [x] 와 동일.
extend(iterable) 리스트의 끝에 이터러블의 모든 항목을 추가. a[len(a):] = iterable 와 동일
insert(i, x) 주어진 위치에 항목을 삽입. 첫 번째 인자는 삽입 위치(인덱스). 두 번째 인자는 삽입할 값.
a.insert(len(a), x)와 a.append(x) 와 동일함.
remove(x) 리스트에서 값이 x첫 번째 항목을 삭제.
pop([i]) 리스트에서 주어진 위치에 있는 항목을 삭제하고, 그 항목을 return.
인덱스를 지정하지 않으면
리스트의 마지막 항목을 삭제하고 return.
clear() 리스트의 모든 항목을 삭제. del a[:] 와 동일
index(x[, start[, end]]) 리스트에 있는 항목 중 값이 x 와 같은 첫 번째 항목의 인덱스를 return.
count(x) 리스트에서 x 의 전체 건수를 return.
list.sort(key=None, reverse=False) 리스트의 항목을 정렬.
reverse() 리스트의 요소의 순서를 역으로 변경.
copy() 리스트의 사본 반환. a[:] 와 동일.

 

 

리스트 컴프리헨션 (Comprehension)

>>> squares = [x**2 for x in range(10)] 
>>> squares 
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> squares = list(map(lambda x: x**2, range(10))) 
>>> squares 
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> squares = [] 
>>> for x in range(10): 
...        squares.append(x**2)
... 
>>> squares 
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 

 

>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
>>> combs = [] 
>>> for x in [1,2,3]: 
...         for y in [3,1,4]: 
...             if x != y: 
...                  combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

 

>>> matrix = [
...         [1, 2, 3, 4],
...         [5, 6, 7, 8],
...         [9, 10, 11, 12],
... ]
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
>>> transposed = []
>>> for i in range(4):
...          transposed.append([row[i] for row in matrix])
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
>>> transposed = []
>>> for i in range(4):
...         transposed_row = []
...         for row in matrix:
...             transposed_row.append(row[i])
...         transposed.append(transposed_row)
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
>>> list(zip(*matrix))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

 

>>> vec = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

 

 

튜플

  - 불변인 시퀀스형.
  - 괄호나 tuple 함수를 사용하여 생성.

>>> tu = 12345, 54321, 'hello!'
>>> tu
(12345, 54321, 'hello!')
>>> tu[0]
12345
>>> v1, v2, v3 = tu
>>> v1
12345
>>> v2
54321
>>> v3
'hello!'
>>> tu = tuple('abcde')
>>> tu
('a', 'b', 'c', 'd', 'e')

 

집합

  - 중복 없는 유일한 값들이 순서에 상관없이 저장되는 컬렉션.
  - 중괄호{}나 set() 함수를 사용하여 생성.

>>> chr1 = {'a', 'z', 'z', 'r', 'd', 'j', 'd'}
>>> chr1
{'j', 'd', 'r', 'z', 'a'}
>>> chr2 = set('arojxg')
>>> chr2
{'j', 'o', 'g', 'r', 'x', 'a'}
>>> chr1 - chr2
{'z', 'd'}
>>> chr1 & chr2
{'j', 'r', 'a'}
>>> chr1 | chr2
{'j', 'o', 'd', 'x', 'r', 'z', 'g', 'a'}
>>> chr1 ^ chr2
{'o', 'x', 'd', 'z', 'g'}

 

>>> a = {x for x in 'abracadabra' if x not in 'abc'} 
>>> a 
{'r', 'd'}

 

 

딕셔너리

  - 매핑 데이터 형. 가변형 임.
  - {키:값} 쌍의 형태로 관리되며, 키로 인덱싱함.
  - 중괄호{}나 dict() 함수를 사용하여 생성. 

>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'jack': 4098, 'sape': 4139, 'guido': 4127}
>>> tel['jack']
4098
>>> list(tel)
['jack', 'sape', 'guido']
>>> sorted(tel)
['guido', 'jack', 'sape']

 

>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}

 

 


사용자 함수 

>>> def fib(n): 
...        """Print a Fibonacci series up to n. <-- Docstring"""
...         a, b = 0, 1 
...         while a < n: 
...             print(a, end=' ') 
...              a, b = b, a+b 
...         print() 
... 
>>> 
>>> fib(2000) 
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
>>> def fib2(n, a=0, b=1): 
...          """Return a list containing the Fibonacci series up to n.""" 
...          result = [] 
...          n1, n2 = a, b 
...          while n1 < n:
...               result.append(n1)
...               n1, n2 = n2, n1+n2 
...          return result 
... 
>>> f100 = fib2(100) 
>>> f100 
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> def cheeseshop(kind, *arguments, **keywords): 
...         print("-- Do you have any", kind, "?") 
...         print("-- I'm sorry, we're all out of", kind) 
...         for arg in arguments: 
...             print(arg) 
...         print("-" * 40) 
...         for kw in keywords: 
...             print(kw, ":", keywords[kw]) 
...
>>> cheeseshop("Limburger", "It's very runny, sir.", 
           "It's really very, VERY runny, sir.", 
           shopkeeper="Michael Palin", 
           client="John Cleese", 
           sketch="Cheese Shop Sketch") 
-- Do you have any Limburger ? 
-- I'm sorry, we're all out of Limburger 
It's very runny, sir. 
It's really very, VERY runny, sir. 
---------------------------------------- 
shopkeeper : Michael Palin 
client : John Cleese 
sketch : Cheese Shop Sketch

 

 


모듈

  - 함수, 클래스, 변수가 저장된 파일. import 하여 사용.
  - 파일명 형식 : [패키지명\]모듈명.py
  - 모듈 검색 경로
     1) 내장 모듈 검색
     2) sys.path
        . 입력 스크립트를 포함하는 디렉터리 (또는 파일이 지정되지 않았을 때는 현재 디렉터리).
        . PYTHONPATH (디렉터리 이름들의 목록, 셸 변수 PATH 와 같은 문법).
        . 설치 의존적인 기본값

${PYTHONPATH}\pkg\fibo.py
# Fibonacci numbers module

def fib(n):    # write Fibonacci series up to n
    a, b = 0, 1
    while a < n:
        print(a, end=' ')
        a, b = b, a+b
    print()

def fib2(n):   # return Fibonacci series up to n
    result = []
    a, b = 0, 1
    while a < n:
        result.append(a)
        a, b = b, a+b
    return result

if __name__ == "__main__":  #__main__ : executed by script
    import sys 
    fib(int(sys.argv[1]))

 

 

# 패키지명 : pkg, 모듈명 : fibo

>>> import pkg.fibo
>>> pkg.fibo.fib(1000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> pkg.fibo.fib2(100)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> pkg.fibo.__name__
'pkg.fibo'
>>> from pkg.fibo import fib, fib2
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
>>> fib2(50)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

 - 모듈을 스크립트로 실행

D:\90_Workspace_Python\pkg>python fibo.py 50

 

 


클래스

  - 파이썬 클래스는 객체 지향형 프로그래밍의 모든 표준 기능들(클래스 상속, 매서드 재정의 등)을 제공

${PYTHONPATH}\myclass.py
class MyClass:

    def __init__(self, msg):
        print("Init message of MyClass")
        self.__msg = msg

    def setmsg(self, msg):
        self.__msg = msg
    
    def printmsg(self):
        print(self.__msg)
    
    def getmsg(self):
        return self.__msg


class MyClassSub(MyClass):

    def __init__(self, msg):
        print("Init message of MyClassSub")
        super().__init__(msg)
    
    def printmsg(self):
        print("Message is \"{}\"".format(super().getmsg()))

>>> from myclass import MyClass, MyClassSub
>>> m = MyClass('test...')
Init message of MyClass
>>> m.printmsg()
test...
>>> m.getmsg()
'test...'
>>> m.setmsg('new message')
>>> m.printmsg()
new message
>>> m.getmsg()
'new message'
>>> ms = MyClassSub('Sub Message')
Init message of MyClassSub
Init message of MyClass
>>> ms.printmsg()
Message is "Sub Message"
>>> ms.getmsg()
'Sub Message'

  - __init()__ : 클래스의 생성자 실행 시 자동으로 init__() 를 호출. 

 

 


입출력

  - 포맷 문자열 리터럴(formatted string literal) 또는 f-문자열 (f-string)
     . 'f' 나 'F' 를 앞에 붙인 문자열 리터럴
     . 치환 필드 포함 가능
     . 중괄호 {} 로 구분되는 표현식
     . 실행시간에 계산되는 표현식

>>> year = 2020
>>> event = 'Referendum'
>>> f'Results of the {year} {event}'
'Results of the 2020 Referendum'
>>> yes_votes = 42_572_654
>>> no_votes = 43_132_495
>>> percentage = yes_votes / (yes_votes + no_votes)
>>> '{:-9} YES votes {:2.2%}'.format(yes_votes, percentage)
' 42572654 YES votes 49.67%'
>>> import math
>>> print(f'The value of pi is approximately {math.pi:.3f}.')
The value of pi is approximately 3.142.
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for name, phone in table.items():
...         print(f'{name:10} ==> {phone:10d}')
...
Sjoerd     ==>       4127
Jack        ==>       4098
Dcab       ==>       7678
>>> print('We are the {} who say "{}!"'.format('knights', 'Ni'))
We are the knights who say "Ni!"
>>> print('{0} and {1}'.format('spam', 'eggs'))
spam and eggs
>>> print('{1} and {0}'.format('spam', 'eggs'))
eggs and spam
>>> print('This {food} is {adjective}.'.format(
...     food='spam', adjective='absolutely horrible'))
This spam is absolutely horrible.
>>> print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred', other='Georg'))
The story of Bill, Manfred, and Georg.
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
...            'Dcab: {0[Dcab]:d}'.format(table))
Jack: 4098; Sjoerd: 4127; Dcab: 8637678
>>> for x in range(1, 6):
...         print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')
...         print(repr(x*x*x).rjust(4))
...
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125

 

 

파일 입출력

  - open(filename, mode)
    . mode : r (readonly, 기본 값), w (writeonly), a (append), r+ (read and write), b (binary mode)

>>> f = open('d:\\testfile', 'r+')
>>> read_data = f.read()
>>> read_data
'This is the first line of the file.\nSecond line of the file\n'
>>> f.readline()
''
>>> f.seek(0)
0
>>> f.readline()
'This is the first line of the file.\n'
>>> f.readline()
'Second line of the file\n'
>>> f.readline()
''
>>> f.write('This is a test\n')
15
>>> f.seek(0)
0
>>> f.read()
'This is the first line of the file.\nSecond line of the file\nThis is a test\n'
>>> f.closed
False

 

 


예외 처리

>>> import sys
>>>
>>> try:
...         f = open('myfile.txt')
...         s = f.readline()
...         i = int(s.strip())
... except OSError as err:
...         print("OS error: {0}".format(err))
... except ValueError:
...         print("Could not convert data to an integer.")
... except:
...         print("Unexpected error:", sys.exc_info()[0])
...         raise
...
OS error: [Errno 2] No such file or directory: 'myfile.txt'

 

 

 


파이썬 스타일 가이드

 

PEP 8 -- Style Guide for Python Code

The official home of the Python Programming Language

www.python.org

 

  • 들여쓰기 단위 : 공백 4칸. 탭 사용 불가.

  • 한 줄 길이 : 최대 79자.

  • 함수, 클래스, 함수 내의 큰 코드 블록 사이는 빈 줄을 넣어 구분.

  • 가능하다면, 주석은 별도의 줄로 입력.

  • DocString 사용을 권장.

  • 연산자들 앞/뒤와 콤마 뒤에는 공백 입력.
    괄호 바로 안쪽에는 공백 불가.
    예) a = f(1, 2) + g(3, 4)

  • 명명 규칙
    . 상수 : UPPER_CASE_WITH_UNDERSCORES.
    . 패키지 : 짧은 소문자 이름 권고. 밑줄은 사용 불가.
    . 모듈 : 짧은 소문자 이름 권고. 밑줄은 사용가능하지만 지양할 것.
    . 클래스 : UpperCamelCase.
    . 함수와 메소드 : lowercase_with_underscores.
    . 메소드의 첫 번째 인수는 항상 self를 사용.
    . 클래스 메소드의 첫 번째 인수에는 항상 cls를 사용.

  • 파이썬의 배포판 코드는 항상 UTF-8이나 파이썬2의 ASCII를 사용할 것.

 

[Ref.] https://docs.python.org/ko/3/tutorial/index.html
        https://docs.python.org/3/tutorial/index.html

 

  CentOS 8 환경(VirtaulBox를 이용한 가상 환경)에서 Spark를 설치해보겠습니다.

  이 포스트를 작성하는 시점에 최종 릴리즈 버전은 2.4.6이지만,
  테스트 환경의 자바 버전이 JDK 7인 관계로 2.1.3 버전을 설치하고자 합니다.

  설치 환경 정보는 아래와 같습니다.

- Spark : 2.1.3
- Hadoop : 2.10.0
- OS : CentOS 8 (VirtualBox 6)
- Java : JDK 7 

 

1. Spark 설치 파일 다운로드

  Spark 설치 파일을 다운로드하기 위해 아래 spark 다운로드 사이트에 접속합니다.

  *  URL : https://spark.apache.org/downloads.html

  이번에 설치할 Spark 2.1.3 버전은 최신 릴리즈 버전이 아니므로 홈페이지 하단에 'Spark release archives'를 클릭하여 아카이브 페이지에 접속합니다.

 

  아카이브의 Spark-2.1.3 하위 디렉토리에 접속하면 위와 같은 설치 파일을 확인하실 수 있습니다.

  이 포스트에서는 Hadoop이 설치된 환경에서 Spark를 설치할 계획이므로 'spark-2.3.4-bin-without-hadoop.tgz' 파일을 사용하도록 하겠습니다.

 

  위 설치파일의 링크 주소를 복사한 뒤 wget을 이용해 파일을 로컬에 다운로드합니다.

 

> sudo wget https://archive.apache.org/dist/spark/spark-2.1.3/spark-2.1.3-bin-without-hadoop.tgz

  참고로 Hadoop 설치 방법은 아래 포스트에서 확인할 수 있습니다.

 

하둡(Hadoop) 설치하기[#1] - 설치 준비

데스크탑이 너무 느려져서 포맷을 했더니, VirtualBox에서 하둡 관리노드가 있던 가상 OS만 복구가 안되는 상황을 맞이하게 되었습니다.;; 이번 기회에 다시 한번 하둡을 설치하면서 여기에 그 과정

sparkdia.tistory.com

 

 

2. 설치 파일 압축 해제

  다운로드 한 파일을 압축해제합니다.

 

  > sudo tar -xvzf ./spark-2.1.3-bin-without-hadoop.tgz

 

  압축 해제된 파일의 소유자를 변경하고 디렉토리명을 spark-2.1.3으로 변경합니다.

  > sudo chown -R hduser:hadoop ./spark-2.1.3-bin-without-hadoop
  > sudo mv spark-2.1.3-bin-without-hadoop spark-2.1.3

 

 

3. 환경 변수 설정

  사용자 홈 디렉토리 내 .bashrc 파일을 열어 아래와 같이 환경변수와 PATH를 추가해줍니다.

export SPARK_HOME=/usr/local/spark-2.1.3
export PATH=$PATH:$SPARK_HOME/bin

 

  하둡이 포함되지 않은 설치 파일을 다운로드한 경우 하둡의 CLASSPATH를 설정해줘야 합니다.

  $SPARK_HOME/conf/spark-env.sh 파일을 생성하고 아래와 같이 입력합니다.

export SPARK_DIST_CLASSPATH=$(${HADOOP_HOME}/bin/hadoop classpath)

 

 

4. 실행 테스트 1

  ${SPARK_HOME}/bin./spark-shell을 실행하여 설정이 정상적으로 완료되었는지 확인해봅니다.

  > spark-shell

  위와 같은 Welcome 메시지가 출력되면 정상적으로 설정된 것입니다.

 

  만약, spark-shell 실행 시 아래와 같은 에러 로그가 출력된다면 SPARK_DIST_CLASSPATH 설정이 잘못된 것입니다.

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/hadoop/fs/FSDataInputStream

 

  위와 같은 에러가 발생한다면 점검을 위해 직접 쉘 프롬프트에서 classpath 설정 구문을 테스트해보는 것도 하나의 해결 방법이 될 것입니다.

  > export SPARK_DIST_CLASSPATH=$(${HADOOP_HOME}/bin/hadoop classpath)
  > echo $SPARK_DIST_CLASSPATH

 

  아래와 같이 실제로 hadoop 명령어가 제대로 작동되는지 확인하는 것도 하나의 해결 방법이 될 수 있습니다.

 

 > ${HADOOP_HOME}/bin/hadoop classpath

 

 

5. Spark 서비스 시작 및 종료

  ${SPARK_HOME}/sbin/start-all.sh을 실행하면 Spark 서비스를 시작할 수 있습니다.

  > ${SPARK_HOME}/sbin/start-all.sh

  jps 실행 시 Master와 Wroker 프로세스가 확인된다면 정상적으로 Spark 서비스가 시작된 것입니다.

 

  웹 브라우저에서 아래 URL(기본 포트 : 8080)을 통해 Spark Web UI를 확인하실 수 있습니다.

* URL : http://서버IP:8080

 

  Spark 서비스 종료는 ${SPARK_HOME}/sbin/stop-all.sh을 실행하면 됩니다.

  > ${SPARK_HOME}/sbin/stop-all.sh

 

 

6. 실행 테스트 2

  spark-submit을 통해서 작성된 Spark application을 실행할 수 있습니다.

  Spark에서 제공하는 SparkPi 샘플 프로그램을 yarn 환경에서 cluster 모드로 실행하는 명령어는 아래와 같습니다.

spark-submit --class org.apache.spark.examples.SparkPi \
--master yarn \
--deploy-mode cluster \
${SPARK_HOME}/examples/jars/spark-examples*.jar \
10

  위 명령어를 실행하면 아래와 같이 Hadoop resource manager 웹 페이지에서 spark application이 실행된 것을 확인하실 수 있습니다.

 

 

[Ref.] https://spark.apache.org/docs/2.1.3/hadoop-provided.html

 

'BigData > Spark' 카테고리의 다른 글

스파크(Spark) 3.0.0 설치 on Windows 10  (0) 2020.06.28
스파크(Spark) 실행하기 on Databricks  (2) 2020.06.22

 

  하이브에서는 다양한 형식의 파일을 읽고 저장할 수 있도록 테이블 별로 파일 형식을 지정할 수 있습니다.

 

 

하이브(Hive) 파일 형식

하이브에서는 테이블 생성 시 'STORED AS 저장형식명' 옵션을 사용하여 테이블별로 파일 형식을 지정할 수 있습니다. 파일 형식을 그냥 쉽게 표현하자면 하이브에서 테이블 형태로 보여지는 데이��

sparkdia.tistory.com

  하이브에서 기본적으로 제공하는 파일 형식 중에서 현재 가장 많이 사용되는 것이 ORC 파일 형식입니다.

  ORC 파일 형식은 컬럼 기반으로 데이터를 저장하며, 기존의 컬럼 기반 저장 방식을 사용하는 RCFile을 개선한 파일 형식입니다. ( ※ 컬럼 기반 저장 방식은 위 하이브(Hive) 파일 형식 포스트를 참고 바랍니다. )

 

   RCFile과 비교하였을 때 ORC의 장점은 아래와 같습니다.

- 각 태스크의 결과가 하나의 파일로 생성되어 네임노드의 부하가 감소됨.
- Datetime, Decimal, complex types (struct, list, map, and union)등의 하이브 데이터 타입을 지원함.
- 파일에 경량 인덱스가 저장됨.
. 필터링 조건에 부합하지 않는 로우 그룹은 skip함. 
. 특정 row로 바로 탐색이 가능.
- 데이터 타입 기반의 block-mode 압축. 
. Integer 컬럼은 run-length encoding을 사용함.
. String 컬럼은 dictionary encoding을 사용함.
- 하나의 파일을 여러 개의 레코드리더를 사용해 동시 읽기 가능함
- 마커 스캐닝 없이 파일 분할이 가능함.
- 읽거나 쓰기에 필요한 메모리의 용량을 제한 가능함
- 메타 데이터를 Protocol Buffers를 사용해 저장함으로써 필드의 추가/삭제가 가능함.

 

 

파일 구조

  ORC의 파일 구조는 아래와 같습니다.

 

Stripe

  컬럼 기반 저장 방식에서는 저장 시 테이블의 데이터를 우선 수평적으로 분할합니다. 분할 기준은 로우 그룹의 크기입니다. 분할된 각각의 로우 그룹 내 데이터를 컬럼 기준으로 저장하게 됩니다. 

  이렇게 분할된 로우 그룹이 Stripe이며, Index Data, Row Data, Stripe Footer로 구성됩니다.

 

Index data

  Index data에는 각 컬럼의 최솟값, 최댓값과 각 컬럼 내 로우의 위치를 저장하고 있습니다. 

  필터링 조건에 의해 테이블의 일부 데이터만 추출하는 경우, 이 Index data를 참고하여 조건에 맞지 않은 로우 그룹은 skip하고 조건에 맞는 로우 그룹만 액세스하게 됩니다.

 

Row data

  실제 테이블 데이터가 저장되어 있습니다.

 

Stripe footer

  스트림 위치 정보가 저장되어 있습니다.

 

File footer

  File footer에는 아래와 같은 보조 정보가 저장되어 있습니다.

  • 파일 내 stripe의 목록
  • stripe별 로우의 수
  • 각 컬럼의 데이터 타입
  • 컬럼 레벨별 통계 정보(건수, 최솟값, 최댓값, 합계)

 

Postscript

  파일의 가장 끝에 위치하며, 압축된 footer의 크기와 압축 파라미터 정보가 저장됩니다.

 

 

 

Integer 컬럼 직렬화 (Integer Column Serialization)

  ORC에서 Interger 컬럼은 두 개의 스트림, bit stream(해당 값이 not-null인지 표현)과 data stream(integer의 스트림)으로 직렬화(serialize)됩니다.

  Interger 데이터는 아래와 같이 숫자의 특징에 따른 encoding 방식이 적용되어 직렬화됩니다.

  •   interger 값이 작아 데이터의 크기가 경량인 경우 variable-width encoding을 사용.
  •   값이 반복되는 경우 run-length encoding을 사용.
  •   정수 값의 범위가 -128에서 127인 경우 run-length encoding을 사용.

  

Variable-width encoding 

  Variable-width encoding은 Google의 프로토콜 버퍼 기반입니다.

  가변 폭 데이터를 인코딩하므로 각 Integer 값의 byte 크기는 각각 상이할 것입니다. 인코딩되는 데이터의 크기가 서로 다른 값들을 구분하기 위해 각 byte의 최상위 bit를 사용합니다. 각 byte의 최상위 bit는 현재 byte가 인코딩하는 데이터의 마지막 바이트를 나타냅니다. 하위 7bit는 인코딩된 데이터를 나타냅니다.

 

Run-length encoding

  Run-length encoding은 반복되는 문자를 반복회수로 표현하는 압축 방법입니다.

  예를들어, 문자열 'AAAAABBBCCCCCCCCDDDEEEEEEEE'인 경우에 Run-length encoding을 적용하면 '5A3B7C3D8E'로 인코딩 됩니다.

 

 

 

문자열 컬럼 직렬화 (String Column Serialization)

문자열 컬럼은 컬럼의 unique한 값들로 구성된 딕셔너리를 사용하여 직렬화됩니다. 딕서너리 내 값들은 정렬되어 있어 predicate filtering의 속도가 높아지고 압축률이 향상됩니다.

  문자열 컬럼은 아래 4개의 스트림으로 직렬화됩니다. 

  •   bit stream : 값이 not-null인지 표현
  •   dictionary data : 문자열의 크기(byte)
  •   dictionary length : 각 항목의 길이
  •   row data : 로우의 값 

 

 

압축 (Compression)

  ORC 파일 포맷을 사용하는 테이블의 모든 스트림은 지정된 코덱을 사용하여 압축됩니다. 압축 코덱은 테이블 생성 시 TBLPROPERTIES 옵션 내 orc.compress라는 key 값을 정의하여 지정 가능합니다. 코덱은 ZLIB, SNAPPY을 사용하거나 none을 선택할 수 있습니다.

  아래 Hortonworks(현재는 Cloudera로 흡수되었죠.)에서 성능 테스트를 한 결과를 보면 ORC 파일 형식이 다른 파일 형식에 비해 상당히 높은 압축률을 보여주고 있음을 확인할 수 있니다. 

[출처] https://blog.cloudera.com/orcfile-in-hdp-2-better-compression-better-performance/

 

 

 

[Ref.] https://cwiki.apache.org/confluence/display/Hive/LanguageManual+ORC#LanguageManualORC-HiveQLSyntax

    https://blog.cloudera.com/orcfile-in-hdp-2-better-compression-better-performance/

 

  하이브에서는 테이블 생성 시 'STORED AS 저장형식명' 옵션을 사용하여 테이블별로 파일 형식을 지정할 수 있습니다.

 

  파일 형식을 그냥 쉽게 표현하자면
  하이브에서 테이블 형태로 보여지는 데이터를
  어떤 구조로 HDFS에 저장하며,
  어떤 압축 방식을 사용할 것인지 정의한 저장 방식이라고 보시면 됩니다.

 

  하둡 개발자 입장에서는 이전 포스트(맵리듀스(Map-Reduce) 상세 작동 방법)에서 언급했었던 InputFormat과 OutputFormat을 확장하여 구현한 것이며, 추가로 <key, value> 형태의 데이터를 레코드 형태로 보여주는 SerDe까지 구현 한 라이브러리라고 보시면 이해가 쉬울 것 같습니다.

 

맵리듀스(Map-Reduce) 상세 작동 방법

맵리듀스(Map-Reduce) 데이터 흐름 하둡 맵리듀스는 클러스터 환경에서 대량의 데이터를 병렬로 처리하는 응용 프로그램을 쉽게 작성할 수 있는 소프트웨어 프레임워크입니다. 맵리듀스 작업은 일

sparkdia.tistory.com

 

  파일 형식기본 값TEXTFILE이며(hive.default.fileformat 옵션으로 변경 가능), 그 외에도 하이브에서는 아래와 같이 다양한 파일 형식을 제공하고 있습니다.

TEXTFILE
SEQUENCEFILE
RCFILE
ORC
PARQUET
AVRO  
JSONFILE

  물론, 하이브는 오픈 소스이므로 사용자 정의의 파일 형식도 적용할 수도 있습니다.

  이 포스트에서는 Hive에서 기본적으로 제공하고 있는 파일형식에 대해 살펴보도록 하겠습니다.

 

1. TEXTFILE

  말 그대로 텍스트 파일입니다. 우리가 메모장이나 vi에디터를 통해서 읽을 수 있는 그 텍스트 파일 맞습니다.

  일반적으로 TEXTFILE 파일 형식은 External 테이블 생성 시 사용합니다. 원천 시스템에서 수집한 데이터나 테스트 등의 목적으로 사용자가 생성한 데이터를 Hive에서 조회하기 위해서는 데이터를 텍스트 파일 형태로 HDFS에 저장한 뒤 External 테이블을 생성하면 됩니다.

  텍스트 파일 내 데이터는 구분자로 필드와 로우가 구별되어야 하며, 테이블 생성 시 DELIMITED 옵션을 사용하여 구분자를 명시해주면 됩니다.

[출처] https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-StorageFormats

 

  TEXTFILE은 기본적으로 압축되지 않은 파일형식이기 때문에(GZip같은 툴로 압축될 수는 있습니다.) 다른 파일형식에 비해 저장 공간을 많이 필요로한다는 단점이 있습니다.
  지속적으로 ODS 영역에 보관할 원천 시스템 데이터라면 압축된 파일형식의 테이블을 생성하여 데이터를 이관하여 보관해야 합니다. 압축된 파일형식의 테이블을 사용하는 것이 쿼리 성능과 저장 공간 활용도를 높이는 방법입니다. 

 

 

2. SEQUENCEFILE 

  SEQUENCEFILE 파일 형식은 데이터를 압축하여 저장합니다.

  SEQUENCEFILE 파일 형식은 로우 기반으로 데이터를 저장합니다.

  위 특징 때문에 코드 테이블, 디멘전 테이블처럼 데이터와 컬럼이 적은 테이블에 적용합니다. 

 

 

3. RCFile (Record Columnar File)

  RCFile은 컬럼 기반으로 데이터를 압축하여 저장하는 파일 형식입니다.

  아래 테이블의 데이터를 예를 들어 로우 기반(Row-oriented)컬럼 기반(Column-oriented) 저장 방식 차이점을 살펴보도록 하겠습니다. 

EmpId Lastname Firstname Salary
10 Smith Joe 40000
12 Jones Mary 50000
11 Johnson Cathy 44000
22 Jones Bob 55000

 

  아래 그림은 위 예제 데이터를 로우 기반 테이블과 컬럼 기반 테이블에 각각 저장하는 경우 저장되는 데이터 순서를 나타내는 예제입니다.

  로우 기반 저장 방식은 아래 예제처럼 데이터의 각 로우 내 데이터를 순서대로 저장(Serialize) 합니다. (각 로우별 저장 순서는 테이블 조회 시 보여지는 로우의 순서와 상이할 수 있습니다.)

001:10,Smith,Joe,40000;002:12,Jones,Mary,50000;003:11,Johnson,Cathy,44000;004:22,Jones,Bob,55000;

  위 방식은 로우의 전체 컬럼 데이터를 한번에 액세스 하는 경우에 효과적입니다.

  대표적으로 Oracle이 로우 기반 방식으로 데이터를 저장합니다.

 

  컬럼 기반 저장 방식아래 예제처럼 한 컬럼의 데이터를 먼저 저장(Serialize)한 후 다음 컬럼의 데이터를 저장합니다.

10 : 001,12 : 002,11 : 003,22 : 004; Smith : 001, Jones : 002, Johnson : 003, Jones : 004; Joe : 001, Mary : 002, Cathy : 003, Bob : 004; 40000 : 001,50000 : 002,44000 : 003,55000 : 004;

  만약에 컬럼 내 동일한 데이터가 존재한다면, 아래와 같은 방법으로 동일한 데이터를 한번만 저장하여 저장 공간을 절약할 수도 있습니다.

...; Smith : 001, Jones : 002,004, Johnson : 003; ...

 

  컬럼 기반 저장 방식은 아래와 같은 장점이 존재합니다.

  • 열 단위인 경우 데이터가 더 균일하므로 압축률이 향상됩니다. 압축률의 향상되면 부수적으로 저장 공간이 절약되고 액세스 시 Disk I/O가 감소됩니다.
  • 쿼리 시 쿼리문에 명시된 컬럼이 저장된 블록만 액세스하면 되므로 I/O가 감소됩니다.
  • 다양한 데이터 액세스 패턴을 적용하는게 수월해집니다.

 

 

 

4. ORC (Optimized Row Columnar)

  ORC는 Full name 그대로 RCFile을 개선시킨 파일 형식입니다. 물론 RCFile과 마찬가지로 ORC도 컬럼 기반 저장 방식을 사용합니다.

  현재 파일 형식중에서 가장 많이 사용되는 파일 형식이라고 말할 수 있습니다.

  ORC에 관해서는 정리할 내용이 많은 관계로 아래 별도의 포스트로 작성하였으니 참고 바랍니다.

 

ORC(Optimized Row Columnar) in 하이브(Hive)

하이브에서는 다양한 형식의 파일을 읽고 저장할 수 있도록 테이블 별로 파일 형식을 지정할 수 있습니다. https://sparkdia.tistory.com/56 하이브에서 기본적으로 제공하는 파일 형식 중에서 현재 가장

sparkdia.tistory.com

 

 

5. PARQUET 

  PARQUET도 컬럼 기반 저장 방식을 사용하는 파일 형식입니다.

  하이브와 피그만 지원되는 ORC에 비해 더 다양한 하둡 에코 시스템을 지원하고 있습니다.

  • Apache Hive
  • Apache Drill
  • Cloudera Impala
  • Apache Crunch
  • Apache Pig
  • Cascading
  • Apache Spark

  그리고 PARQUET는 Google Protocol Buffers와 비슷한 모델을 사용합니다. 이로 인해 복잡하게 중첩된 데이터 구조(List, Maps, Sets)를 효율적으로 저장할 수 있습니다. (참고 : Dremel made simple with Parquet)

 

 


6. AVRO  

  아파치 에이브로(AVRO)는 데이터 직렬화(Serialization) 시스템입니다.
  하이브에서는 이 에이브로의 파일을 하이브에서도 액세스 할 수 있도록 AVRO SerDe를 제공해줍니다.

  에이브로는 하둡의 직렬화 방식(Writable)방식의 단점인 언어 이식성을 해결하기 위해 만들어진 프로젝트 입니다. 데이터 파일에 스키마 정보를 같이 저장하므로 사전에 스키마 정보를 알지 못해도 데이터를 읽는 시점에 스키마 정보를 파악할 수 있게 됩니다. 이러한 방식으로 데이터를 관리하므로 다양한 개발 언어를 이용해 데이터 셋을 공유할 수 있는 것입니다.

  상세한 내용은 아파치 에이브로 홈페이지를 참고하시길 바랍니다.

  

 


7. JSONFILE

  Hive 4.0 버전에서부터 JSONFILE 파일 형식도 지원한다고 하네요. 

  기존에는 사용자 정의의 파일 형식을 이용하거나 전처리 과정을 통해 CSV 파일 형태로 만든 뒤 TEXTFILE 파일 형식을 사용하여 데이터를 액세스했었는데, 이런 번거로움이 사라지겠네요.

 

 

  이상으로 하이브의 파일 형식에 대해 알아보았습니다.
  위 내용중에서 추가/보완해야 할 사항있으면 친철한 댓글 부탁드립니다.
  감사합니다.

 

 

[Ref.] https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-StorageFormats
  https://cwiki.apache.org/confluence/display/Hive/FileFormats
  https://cwiki.apache.org/confluence/display/Hive/LanguageManual
  https://en.wikipedia.org/wiki/RCFile
  https://blog.twitter.com/engineering/en_us/a/2013/dremel-made-simple-with-parquet.html
  Hadoop, The Definitive Guide

 

Windows 환경에서 Tableau를 설치해보겠습니다.

설치 파일 다운로드를 위해 우선 아래 URL에 접속합니다.

URL : https://www.tableau.com/ko-kr/products/desktop/download?signin=47cc9ac0f8435895c744899af5d60d2e

위 URL에 접속 후
아래와 같이 '비즈니스 이메일' 텍스트 박스에 이메일 주소를 입력 후
'무료 평가판 다운로드' 버튼을 클릭하면 설치 파일이 다운로드 됩니다.

 

  설치 파일 다운로드가 완료되었으면, 설치 파일을 실행합니다.

 

  설치 파일을 실행하면 아래와 같은 다이얼로그를 확인할 수 있습니다.

 사용권 계약 동의를 위해 '동의함' 체크 박스를 클릭 후 '설치' 버튼을 클릭합니다.

  다음 다이얼로그를 통해 설치 진행 상황을 확인하실 수 있습니다.

 

설치가 완료되면 자동으로  Tableau Desktop 버전이 실행됩니다.

 

  Tableau 를 처음 실행하면 아래와 같이 사용자 기본 정보를 입력해야 합니다.

 

  사용자 정보를 입력하면 아래와 같이 평가판 활성화 작업이 진행되며,

 

  활성화가 완료되면
  아래와 같이 Tableau가 정상적으로 실행된 것을 확인할 수 있습니다.

 

  초기 화면 하단에 '샘플 통합 문서'를 클릭하면 Tableau에서 제공하는 샘플 데이터를 확인할 수 있습니다.

 

 

 

+ Recent posts