데이터 이전 후 설명 없는 NullPointerException

 ERROR TaskSetManager: Task 0 in stage 5.0 failed 4 times; aborting job
org.apache.spark.SparkException: Job aborted due to stage failure: Task 0 in stage 5.0 failed 4 times, most recent failure: Lost task 0.3 in stage 5.0  : java.lang.NullPointerException
    at org.apache.spark.sql.execution.datasources.orc.OrcColumnVector.getUTF8String(OrcColumnVector.java:167)
    ...

데이터 이전을 잘 하고 나머지 집계도 잘 동작하는지 확인하기 위해 검증하는 도중 원인 설명이 제대로 되지 않은 문제가 발생했다.

원인

이전 이전의 Table에 필요한 columns을 추가하였고, 생성문은 관련된 columns끼리 묶어 관리하기 위해 순서를 변경함

그로 인하여 기존 테이블 스키마 순서와 이전할 테이블의 순서가 다르기 때문에 문제가 발생함.

간단 설명

  1. 아래와 같은 테이블이 존재했다 가정하고 시작해보자

    id passwd name
    id1 passwd1 nane1
    id2 passwd2 nane2
    id3 passwd3 nane3
  2. email칼럼을 추가하기위해 데이터를 추가하였다.

    id passwd name email
    id1 passwd1 nane1 email1
    id2 passwd2 nane2 email2
    id3 passwd3 nane3 email3
  3. 이전한 테이블은 id,passwd,email,name 순으로 테이블 스키마를 정의했다.

  4. create external table if not exists db명.tb명 ( id string , passwd string , email string , name string ) ;

  5. 복제할때 기존 테이블의 스키마 순서인 id,passwd,name,email 가아닌 id,passwd,email,name 로 스키마 순서가 바뀌어 데어터 순서가 뒤섞여 복제가 되었다.

    id passwd email name
    id1 passwd1 nane1 email1
    id2 passwd2 nane2 email2
    id3 passwd3 nane3 email3

해결책

1번 해결책

생성문을 기존 칼럼 순서와 동일하게 만든다.

create external table if not exists db명.tb명 (
      id      string
    , passwd  string
    , name    string
    , email   string
)
;
  • 위에 설명한 원인을 예시로 보면 위와 같이 기존 테이블 순서에 맞게 변경한 후 이관시킨다
  • 하지만 이 방법은 columns끼리 묶으려고 순서를 변경하였기에 썩 좋지 않은 방법이라고 생각한다.

2번 해결책 ( 실패 )

ALTER TABLE test_change CHANGE [ col_name ] [col_new_name] [type] [location] 을 이용하여 순서변경

alter table db명.tb명 change id     id      string first         ;
alter table db명.tb명 change passwd passwd  string after  id     ;
alter table db명.tb명 change name   name    string after  passwd ;
alter table db명.tb명 change email  email   string after  name   ;
  • 실 구동은 해보지 않음 hive cwiki

  • 아래 테이블의 차이처럼 metadata 까지 변경 되는것으로 보이기 때문에 기각

    id passwd email name
    id1 passwd1 nane1 email1
    id2 passwd2 nane2 email2
    id3 passwd3 nane3 email3
    id passwd name email
    id1 passwd1 email1 nane1
    id2 passwd2 email2 nane2
    id3 passwd3 email3 nane3

3번 해결책 ( 성공 )

select a.CD_ID, a.COLUMN_NAME, a.INTEGER_IDX
from hive.COLUMNS_V2 a
inner join hive.SDS b
    on  b.CD_ID = a.CD_ID
inner join hive.TBLS c
    on  c.SD_ID = b.SD_ID
    and c.TBL_NAME = 'table_name'
order by INTEGER_IDX
;
  • 출력 예시

    CD_ID COLUMN_NAME INTEGER_IDX
    504 member_id 0
    504 char_id 1
    504 first_time 2
    504 last_time 3
    504 reg_time 4
  • 위와 같이 metastore 정보를 확인해보면 COLUMN_NAME,integer_idx을 확인할 수 있다.

  • 해당 COLUMN_NAME과 INTEGER_IDX의 값과 순서를 기존 테이블과 동일하게 만들어주면된다.

  • 따로 외래키등이 걸려있지 않아서 해당 CD_ID의 값을 insert overwrite로 해결하였다.

추정되는 원인

기존에 hive.metastore.schema.verification=false로 처리가 되어 있어 schema 검증을 제대로 안 되서 그런 것으로 추정됨

hive --orcfiledump /File_Path 이 명령어를 이용하여 orc 파일의 schema를 확인해보니, 아래와 같다.

  1. hive.metastore.schema.verification=true

    Structure for /File_Path
    File Version: 0.12 with HIVE_8732
    Rows: 1151
    Compression: SNAPPY
    Compression size: 262144
    Type: struct<member_id:string,char_id:string,first_time:timestamp,last_time:timestamp,reg_time:timestamp>
    
    Stripe Statistics:
    Stripe 1:
        ...
    
    File Statistics:
    ...
    
    Stripes:
    ...
  2. hive.metastore.schema.verification=false

    Structure for /File_Path
    File Version: 0.12 with HIVE_13083
    Rows: 1095
    Compression: SNAPPY
    Compression size: 262144
    Type: struct<_col0:string,_col1:string,_col2:timestamp,_col3:timestamp,_col6:timestamp>
    
    Stripe Statistics:
    Stripe 1:
        ...
    
    File Statistics:
    ...
    
    Stripes:
    ...

위 결과를 보면 hive.metastore.schema.verification=true를 해야 orc의 schema값이 Hive의 스키마로 잘 생성된다.

  1. true

    • Type: struct<member_id:string,char_id:string,first_time:timestamp,last_time:timestamp,reg_time:timestamp>
  2. false

    • Type: struct<_col0:string,_col1:string,_col2:timestamp,_col3:timestamp,_col6:timestamp>

데이터 이전 전의 테이블이 false로 되어있던 것 이기 때문에 테스트는 하지 못 하였으나, 겪은 문제처럼 칼럼 순서가 뒤바뀐 이유는 이것으로 추정된다.

spark 2.4.8, scala 2.11

에러 문구

org.apache.spark.sql.AnalysisException: Table or view not found: `dbName`.`tbName`; line 12 pos 9;

Intellij 에서 실행시킬 때 아주 잘 작동하였으나, 테스트 서버에 배포하여 spark-submit하니 위와 같이 해당 테이블을 찾지 못하는 문제가 발생하였다.

개인적으로 확인한 리스트

  1. MetaStore URL 설정여부
    val sparkConf: SparkConf = new SparkConf()
        .set("hive.metastore.uris", AppConfig.hiveMetastoreUrl)
- 위와 같이 `"hive.metastore.uris"`  에 metastore url을 잘 작성해주었나?
    - 당연히 intellij에서 실행이 잘 되는건 잘 넣어줬기 때문이다.
  1. enableHiveSupport() 설정 여부
    SparkSession.builder().config(SparkConfig.sparkConf)
                          .enableHiveSupport()
                          .getOrCreate()
  • 위와 같이 enableHiveSupport()를 잘 넣어 주었나?
    • 당연히 intellij에서 실행이 잘 되는건 잘 넣어줬기 때문이다.
  1. HDFS 권한 문제?
    hdfs dfs -ls /HiveTablePath
  • 따로 설정하지 않았지만, 확인 결과 문제는 없었다.
  1. Hive-site.xml을 제대로 읽지 못 했다?
    spark-submit \
    --class Execute class path \
    --master Mode \
    --jars "Extra library Path/*.jar" \
    --files Hive Site Path/hive-site.xml \
  • 빙고 Spark submit이 hive-site.xml을 읽어오지 않아 발생한 문제였다
    • metastore설정이 잘 되어 있는데 왜 잘 작동하지 못 하는지는 추후 확인 해 봐야겠다.

개인적인 팁

  • 한가지 팁으로는 --jars "Extra library Path/*.jar" ( 잘못된잘못된 정보를 공유하는 것일 수 있으니, 틀린 정보면 알려주시면 감사하겠습니다.)
    • 위 *.jar은 Spark 1.6을 사용할 때 사용불가였지만, 2.4로 넘어오니 동작하였다. (spark 1.6 : "~~/lib1.jar,lib2.jar....")
    • Extra library Path에 Prefix를 넣어 원격지의 jar파일을 가져와서 읽을 수 있다. (e.g: hdfs://///*.jar)
      • 필자 경험상 이렇게 안 하고 로컬의 library를 사용하면, 지속적으로 HDFS에 업로드
      • 위와 같이 실행하는 hdfs에 올려놓으면 업로드 하지 않고, 해당 lib에 링크걸어서 사용하는 것으로 보였다.

필자는 4번으로 해결했다. 이것도 아니였으면 더 많은 것을 찾아봐서 더 많은 오류 해결 방법을 습득했을 것 이지만, 그래도 빠르게 해결되서 다행이다.

버전

  • Spark 2.4.8, Scala 2.11.12

Spark 2.4.8, Scala 2.11.12

회사에서 마이그래이션을 위해서 새로 구현한 Spark 배치/스트리밍을 서버에 배포하여 실행 테스트를 해보니 아래와 같은 에러가 발생하였다.

diagnostics: User class threw exception: java.lang.NoSuchMethodError: scala.Product.$init$(Lscala/Product;)V

위 에러는 Scala의 메소드를 실행시킬 수 없다는 에러 문구다
해당 에러가 발생하는 곳을 따라가보니 발생하는 곳을 확인해보니 case class에서 발생하는 에러였다.

case class TableInfo(serviceType: String, databaseName: String, tableName: String)

확인해보니, 서버에 구축된 Spark의 Scala 버전과 Build시 사용하는 Sacla 버전이 달라 문제가 발생한 것이였다.

spark 2.4.8버전이기때문에 당연히 scala 2.12버전으로 구축 했을 줄 알았는데 확인해보니 Scala 2.11.12 버전으로 설치해 놨었다..

spark 버전 확인하는 방법은 다음과 같다

[~]$ spark-sql --version
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 2.4.8.3.1.0.0-2
      /_/

Using Scala version 2.11.12, Java HotSpot(TM) 64-Bit Server VM, 1.8.0_192

[~]$ spark-shell --version
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 2.4.8.3.1.0.0-2
      /_/

Using Scala version 2.11.12, Java HotSpot(TM) 64-Bit Server VM, 1.8.0_192

다음 부터는 공식홈페이지에 아래와 같이 scala 2.12를 사용해야한다고 해서 당연히 그렇게 구축되어 있다고 생각하면 안 되겠다. 개발전에 구축된 버전을 먼저 확인하고 개발을 진행해야겠다.

Spark runs on Java 8, Python 2.7+/3.4+ and R 3.5+. For the Scala API, Spark 2.4.8 uses Scala 2.12. You will need to use a compatible Scala version (2.12.x).

Spark 2.4.8 공식홈페이지

Version

  1. Spark : 1.6.3
  2. hdfs : 2.7.3
  3. Hive : 1.2.1

기분좋게 휴가를 다녀왔더니 집계 메일이 와있어 호다닥 이슈를 확인해 봤다.

실패한 Spark 집계 및 상당히 느려진 Spark 집계... 당혹스러웠다.

Spark UI를 확인하면 아래와 같다.

image

분명 집계 개선을 하여 3분대로 낮춰놨는데, 당혹스럽게 위 사진과 같이 불필요한 wait 시간이 29분이나 되는 것을 확인할 수있다. total Uptime과 비교하면 실직적으로 집계를 수행한 시간은 1분 남짓인데, 기다린 시간이 29분인 것을 보니 상당히 큰 이슈였다.

그래서 Hadoop Application 에서 로그를 확인해 보니 아래와 같은 문제가 발생하였다.

ERROR Hive: org.apache.thrift.transport.TTransportException: java.net.SocketTimeoutException: Read timed out
    at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:129)
    at org.apache.thrift.transport.TTransport.readAll(TTransport.java:86)
    ...
Caused by: java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method)
    ... 134 more

내부망으로 DataNote 통신을 하는데 TimeOut인 것은 뭔가 문제가 있어보였다.

다른 문제점이 무엇이 있는지 확인할 겸 Ambari를 확인해보니, HiveMetastore에 Alert가 발생하였다 .

        Metastore on ambariserver.coresight.com failed (Traceback (most recent call last):
  File "/var/lib/ambari-agent/cache/common-services/HIVE/0.12.0.2.0/package/alerts/alert_hive_metastore.py", line 203, in execute
    timeout_kill_strategy=TerminateStrategy.KILL_PROCESS_TREE,
  File "/usr/lib/python2.6/site-packages/resource_management/core/base.py", line 166, in __init__
    self.env.run()
  File "/usr/lib/python2.6/site-packages/resource_management/core/environment.py", line 160, in run
    self.run_action(resource, action)
  File "/usr/lib/python2.6/site-packages/resource_management/core/environment.py", line 124, in run_action
    provider_action()
  File "/usr/lib/python2.6/site-packages/resource_management/core/providers/system.py", line 262, in action_run
    tries=self.resource.tries, try_sleep=self.resource.try_sleep)
  File "/usr/lib/python2.6/site-packages/resource_management/core/shell.py", line 72, in inner
    result = function(command, **kwargs)
  File "/usr/lib/python2.6/site-packages/resource_management/core/shell.py", line 102, in checked_call
    tries=tries, try_sleep=try_sleep, timeout_kill_strategy=timeout_kill_strategy)
  File "/usr/lib/python2.6/site-packages/resource_management/core/shell.py", line 150, in _call_wrapper
    result = _call(command, **kwargs_copy)
  File "/usr/lib/python2.6/site-packages/resource_management/core/shell.py", line 297, in _call
    raise ExecuteTimeoutException(err_msg)

내용을 보아하니 대충 Metatore에 문제가 발생하였다는 것을 볼 수 있다.

그럼으로 해당 문제를 확인하기 위해 (Hortnworks기준) /var/log/hive/hivemetastore.log 를 확인해 보니 아래와 같은 엄청난 것을 볼 수 있었다.

image

위와 같은 충격적인 Log를 확인하고, Error문구를 확인하니 아래와 같았다.

com.mysql.jdbc.PacketTooBigException: Packet for query is too large (1048964 > 1048576). You can change this value on the server by setting the max_allowed_packet' variable.
        at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3279)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1971)
        ...

에러 문구를 보니 설정한 max_allowed_packet 보다 많은 양의 packet보내 문제가 발생하였다는 것이였다.

필자는 mysql 을 5.1버전을 사용하고 있는데, Default값이 1MB인지, Hortenworks의 Default값인지 정확하게 확인은 하지 않았지만, 상대적으로 낮은 사이즈로 설정되어있었다. (5.7 버전의 Default값은 4MB였다.)

이것을 해결 하는 방법은 여러가지가 있겠지만, 필자가 생각하는 방법은 2가지가 있다.

  1. 더 작은 패킷을 보낸다

    • 사실상 원하는 해결책이 아님
  2. max_allowed_pachet을 늘린다.

    1. 당장 바꾸고 싶다면 아래와 같이 하면 된다

       SET GLOBAL max_allowed_packet = 33554432;        
       FLUSH PRIVILEGES;        
       show variables where Variable_name = 'max_allowed_packet';
    2. /etc/my.cnf 에 max_allowed_pachet 추가해줌

       [mysqld]
       datadir=/var/lib/mysql
       socket=/var/lib/mysql/mysql.sock
       user=mysql
       # Disabling symbolic-links is recommended to prevent assorted security risks
       symbolic-links=0
       max_allowed_packet=32M
      • 굳이 32MB일 필요는 없다
    3. mysql을 재시작 해주면된다.

여러가지 문제를 경험해야 많은 것을 배울 수 있는 것 같다.

만약 설정된 max_allowed_packet가 1MB가 아니여서 위와 같은 Error가 발생하지 않았다면,
회사에서 5년치 데이터를 한번에 읽어 처리하여 Pachet량이 1mb를 넘기지 않았다면 필자는 해당 문제를 경험하지 않아 고민을 하지 않았을 것 같다.

물론 문제가 발생하여 짜증은 났지만, 해결한 현재는 좋은 경험이였다고 생각한다.

hint: You've added another git repository inside your current repository.
hint: Clones of the outer repository will not contain the contents of
hint: the embedded repository and will not know how to obtain it.
hint: If you meant to add a submodule, use:

위와 같은 에러가 나왔다면, 아래 3단계를 해보면 아마 될 것입니다.

  1. 올리고자 하는 디렉터리에 .git폴더 유무를 판단하고, 있다면 삭제해줍니다.
  2. git rm --cached 머기시를 쳐봅니다.
  3. git rm --cached . -rf를 실행하고 재시도해보면 아마 될 것입니다. ( 저는 성공했습니다. )

위 해결법은 깃안에서 화살표 달려있는 폴더로 표시되서 안으로 못 들어가는 에러도 해결됩니다!

각 단계를 실행하고, commit , push를 시도해 보세요 ㅎ

목차

    장고를 이용하여 도커 오라클 연동 에러.

    오늘 수업을 듣는데 , 오라클 연동이 너무 안돼서 애를 먹었습니다.

    나를 고통받게 했던 에러는 cx_Oracle.DatabaseError: DPI-1080: connection was closed by ORA-12505 입니다.

    다른 분들은 아마 ORA-12505 부분에서 숫자 부분은 매번 다르게 나오는 걸로 알고 있습니다.

    저의 경우에는 tnsnames.oralistener.ora 를 고쳐서 해결하였다.


    경로

    • 도커 : /opt/orcle/product/18c/dbhomeXE/network/admin/
      • tnsnames.oralistener.ora가 들어있는 디렉토리
    • 윈도우 : 오라클 설치경로\18.0.0\dbhomeXE\network\admin 혹은 C:App\admin\product\18.0.0\dbhomeXE\network\admin
      • tnsnames.oralistener.ora가 들어있는 디렉토리

    변경사항

    tnsnames.ora listner.ora
    사용자 지정명 =
      (DESCRIPTION =
        (ADDRESS = (PROTOCOL = TCP)(HOST = host.docker.internal)(PORT = 1521))
        (CONNECT_DATA =
          (SERVER = DEDICATED)
          (SERVICE_NAME = 사용자 지정명)
        )
      )
    DEFAULT_SERVICE_LISTENER = XE
    USE_SID_AS_SERVICE_LISTENER=on

    LISTENER =
      (DESCRIPTION_LIST =
        (DESCRIPTION =
          (ADDRESS = (PROTOCOL = TCP)(HOST = host.docker.internal)(PORT = 1521))
          (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
        )
      )

    • tnsnames.ora
      • 저는 XEPDB1이라고 생성했기 때문에 위 빨간 부분에 XEPDB1을 삽입
        • 하지만 문제는 해결되지 않음.
    • listner.ora
      • USE_SID_AS_SERVICE_LISTENER=on 를 삽입.
        • 에러 해결!!

     

     

     


    위 와같이 해서 에러를 해결돼서 아래와 같이 잘 불러들여졌습니다.

     

    저와 같은 에러를 가지고 있다면 한번 삽입해보시고 해결됐으면 좋겠습니다!

     

     

     

    + Recent posts