목차

반응형

어느날 잠깐 쉬러나왔다가 사무실로 돌아오니까 선배가 심각한표정으로 불렀다.

순간 '아 x됐다 뭐잘못했지?'


나보고 xx씨 이거 xx씨가 업데이트쳤어요?

보니까 내가한거였다.


실서버로 돌아가는 사이트의 단일 데이터를 수정하는 부분인데

분명히! 업데이트 하기전에 '아 나놈은 실수 많이하니까 셀렉트해서 한건 나오는지 확인하고 업데이트 하자!'

그리고 셀렉트문에서 한건 나오는거 확인하고 정작 업데이트할때 WHERE을 생략해버렸다.

업데이트하고나서 그 데이터만 확인해서 다른 데이터들도 바뀐지는 몰랐는데 며칠뒤에 상황을 파악하게 된것이다.

그 결과, 연화장에서 3만건의 유골함 위치(안치장소라고 부른다.)가 하나로 통일되버렸다.

데이터 형식도 4-405-41345 막 이런식이다 데이터를 생성하는 알고리즘도 없고 규칙도없다.

갑자기 머릿속으로 3만 가구 유가족들의 안치장소가 한곳으로 통일되고 우리회사로 전화가 물밀듯이 걸려올 것을 상상하니 바로그냥 머리가 핑 돌았다.


선배님께서 일단 빠르게 복구 방법을 찾아보고 복구 방법이 없으면 차장님께 보고를 하자고 해서 일단 내가 먼저 처리해보고싶었으나 맞는말이어서 그렇게 하기로 했다.


결론적으로 복구 방법이 안보였다.


열심히 찾아보니 대략 3가지 방법을 찾았었다.


1. Ms사에서 제공하는 방법

https://docs.microsoft.com/ko-kr/sql/relational-databases/backup-restore/restore-a-database-to-a-marked-transaction-sql-server-management-studio?view=sql-server-2017

mssql task transaction recovery에 대한 이미지 검색결과

분명히 저 버튼을 누르면 트랜잭션 전으로 복원이 되야하는데 당연히 내가 사고친 DB는 저 서비스가 꺼져있었다.

바닥에 주저앉고싶었지만 다음 방법을 찾았다.


2. 주기적으로 또는 오류가 나면 자동으로 백업해주는 백업데이터를 임포트

https://blog.sqlauthority.com/2010/11/10/sql-server-get-database-backup-history-for-a-single-database/

여태까지 백업한 데이터를 쿼리를 이용하여 조회할수있다.

SQL SERVER - Get Database Backup History for a Single Database backuphistory

위 주소에서 쿼리문을 사용하여 조회시 위와같은 결과가 나온다.

나도 2개의 데이터가 나왔다. 근데 백업날짜가 2017년도 데이터였다.

또다시 바닥에 주저앉고 싶었지만 다른 방법을 찾아봤다.


2-1. 가장 최근에 실행되었던 쿼리 조회

별 의미는 없었으나 그냥 기록해뒀다.

https://social.msdn.microsoft.com/Forums/sqlserver/en-US/7ef42395-0475-4820-969e-b7e7ab297974/recent-executed-queries-for-a-specific-database?forum=transactsql


3. 1번과 2번을 지나쳐보니 x됐다는 생각밖에 안들었다. 데이터는 수정됐는데 백업장치는 하나도 안되있고, 하지만 머리속으로 든 생각이 분명 Ms사에서 만든건데 어딘가에는 무조건 실행한 쿼리에 대해 저장되는 정보가 있을 것 같았다. 뭔가 느낌적인 느낌? 그리고 1번과 2번을 검색해보면서 가끔씩 쿼리에 대한 바이너리 데이터가 보였다. 사람이 위급해지면 초인적인 힘을 발휘한다고 쿼리 데이터와 바이너리를 집중적으로 검색해보았더니 무언가 시도해볼만한 정보가 나왔다.

굉장히 도움이 많이 되었던 사이트

http://rusanu.com/2014/03/10/how-to-read-and-interpret-the-sql-server-log/


sys.fn_dblog에 쿼리의 수행 결과가 바이오스 형태로 저장된다는 것을 찾아보았다.

쿼리를 실행해봤는데 기본적으로 날짜 오름차순으로 정렬되어있어서 데이터가 없는줄알았다.

선배님이 오름차순인것을 알려주셔서 내가 수행했던 쿼리부분을 찾을 수 있었다. (이부분 안찾아주셨으면 그냥 포기하려고했었다.)


쿼리 찾은 사이트

https://social.msdn.microsoft.com/Forums/sqlserver/en-US/75614751-b1d0-4098-b5e5-c722439f5940/correlating-sysdmtransessiontransactions-transaction-id-to-the-fndblog?forum=sqldatabaseengine


아래는 사용했던 쿼리와 문제된 테이블의 sys 데이터 조회 결과다.

USE DB명 go SELECT [Current LSN], [Transaction ID], [Operation], [Transaction Name], [CONTEXT], [AllocUnitName], [Page ID], [Slot ID], [Begin Time], [End Time], [Number of Locks], [Lock Information] FROM sys.fn_dblog(NULL,NULL) WHERE Operation IN ('LOP_INSERT_ROWS','LOP_MODIFY_ROW', 'LOP_DELETE_ROWS','LOP_BEGIN_XACT','LOP_COMMIT_XACT')

굉장히 다급했기에 이해보다는 최대한 감각적으로 접근했다.

현재 나에게 필요한것은 아래와 같았다.

1. Update를 실행하였을때 기존 데이터 값(이 값이 있다면 업데이트 치려고 했던 데이터도 쌍으로 같이 있을거라고 생각했다.)

2. Update를 치려고 한 데이터의 고유 아이디 값(분명 Mssql에도 프로그램에서 사용하기 위한 고유 아이디 값이 있을 것이라고 생각했다.)

3. 확인한 데이터가 정말 내가 실행한 쿼리가 맞다는 증거


Current LSN은 쿼리의 고유 인덱스값인것같고

Transction ID는 트랜잭션 수행 단위별로 부여되는 ID같았다.

딱 눈에 띄는것이 대략 3만건의 트랜잭션 로그가 같은 Transction ID인것을 찾았다.

시간대도 내가 사고쳤을때와 비슷했다. Operation도 LOP_MODIFTY_ROW였다.

이거는 빼박이었다.

뒤지면서 Operation형태는 다음과 같음을 찾았다.

LOP_INSERT_ROWS - 삽입

LOP_MODIFY_ROW - 수정

LOP_DELETE_ROWS - 삭제(ㅅㄱ)

LOP_BEGIN_XACT - 트랜잭션의 시작을 알림

LOP_COMMIT_XACT - 트랜잭션의 종료를 알림 


나 같은경우는 LOP_BEGIN_XACT다음 3만건의 LOP_MODIFY_ROW 이후 LOP_COMMIT_XACT가 발생했다.


여튼 3만건의 문제되는 트랜잭션 로그를 찾았다.


그러면 해당 Transction ID로만 검색을 할 수 있었다.

SELECT [Current LSN], [Transaction ID], [Operation], [Transaction Name], [CONTEXT], [AllocUnitName], [Page ID], [Slot ID], [Begin Time], [End Time], [Number of Locks], [Lock Information], [Num Elements], [RowLog Contents 0], [RowLog Contents 1], [RowLog Contents 2], [RowLog Contents 3], [RowLog Contents 4], [RowLog Contents 5] FROM sys.fn_dblog(NULL,NULL) WHERE [Transaction ID]='트랜잭션 ID값'

조회결과 약 3만건의 데이터를 찾았다.

이후 데이터를 가공해서 쿼리 결과 이미지는 없고 엑셀로만 남아있다.



이 데이터가 내가 원하는 데이터임은 확인했고

확인해야하는 것은 기존 데이터와 레코드의 주소값이었다.


RowLog Contents 0, RowLog Contents 1의 데이터를 cast([RowLog Contents 0] as varchar(20))를 통하여 바이너리를 문자열로 바꿔서 보라는 말이 있어서 확인해보았다.

 cast([RowLog Contents 2] as varchar(20)) -> 나중에 데이터를 추출하기위해 cast로 필드를 하나씩 더만들어줬다.

급한대로 Hex코드를 문자열로 바꾸는 사이트에서 돌려보았다.

https://codebeautify.org/hex-string-converter


이때 딱 느낌이 왔다. 아 이거 살릴수있겠다. 자기감탄은 그만하고

약 3만건의 데이터들의 Hex값이 아래와 같았다.

0x312D3130332D3031363939 0x342D3430352D3138383838
0x312D3130422D3231383032 0x342D3430352D3138383838
0x302D3030322D3234383936 0x342D3430352D3138383838
0x312D3130342D3032333339 0x342D3430352D3138383838
0x302D3030322D3235313536 0x342D3430352D3138383838
0x312D3130342D3032333232 0x342D3430352D3138383838
0x332D3330312D3131383230 0x342D3430352D3138383838
0x302D3030422D3237313230 0x342D3430352D3138383838
0x302D3030332D3235333139 0x342D3430352D3138383838
0x302D3030372D3031383431 0x342D3430352D3138383838
0x312D3130362D3033383332 0x342D3430352D3138383838

딱봐도 왼쪽은 기존값이고 오른쪽이 내가 실수로 일괄 업데이트 친것같았다.


해석해보니 맞다.


그럼 이제 남은건 업데이트를 다시 치기위해 기존 데이터의 record location을 찾는 것이었다.

찾아보니 %%lockres%%라는 필드가 데이터의 고유 주소값을 가리킨다는 것을 찾았다.

확인해보고싶으면

select %%lockres%%, *

from 테이블

을 실행해보면 확인이 가능하다.


문제는 레코드의 위치데이터를 뽑아내야한다는 것이었다.

Lock Information을 확인해보면 ACQUIRE_LOCK_X RID: 6:1:19055:0가 lockers 데이와 일치한다는 것을 알 수있다.

하지만 문제가 생겼다.(문제는 ㄹㅇ 수도없이 생겼다)

갑자기 6000번대 데이터부터 ACQUIRE_LOCK_X 가 사라진것이다.

그 다음 데이터들도 뭔가를 가지고 있긴 한데 유효한 정보가 아니었다.

흠... 그래서 열심히 짱구를 굴려보니까 규칙을 찾을 수 있었다.


요 데이터의 조합이 바로 ACQUIRE_LOCK_X 값이라는 것을 알아냈다.

예를들어 0001:0000010d 1은

16진수를 10진수로 변환을 통해서

1:269:1 이라는 데이터의 조합을 가지게 된다.


그러면 다찾았다.

위치데이터, 이전 정보값


그리고나서 또 심각한 문제가 발생했다.

(참고로 해석1은 데이터를 복원한 결과라서 원래는 엄청 잘려서 나온다. 원본1, 원본2를 주목하자)

분명히 업데이트 칠때 기존값, 변경값 데이터가 제대로 나와야하는데

기존값은(원본1) 이상하다고 쳐도 변경값(원본2)은 일정한 값으로 전부다 업데이트 쳤기에 다르면 안되는 것이지만 꽤나 많은 데이터가 깨진다는 것을 발견했다.


인터넷에 깨진 데이터의 원인을 도무지 찾아봐도 나오지않아서 데이터를 보고 규칙을 찾아서 복원하기로 했다.

한번 살펴보니 규칙이 있었다.

숫자 8이 들어가면(38) 해당 값이 없어졌다.(정확히는 기존값과 변경값의 문자열이 연속으로 일치하면 사라졌다.)

그래서 만약에 원본2의 값에서 3838이 사라지면 해석1에 사라진 위치에 숫자 88을 붙여주는 방식으로 복원을 하기로 하였다.


그러면 데이터는 전부다 찾았는데 이제 어떻게 하면 3만건의 업데이트문을 만들까 생각을 해보았다.

항상 이런거 생각하는게 재밌다.


이번에는 엑셀을 사용해봤다.

Update문을 작성하기 위해 필요한 데이터는

원본값, 레코드 주소 두가지였다.

UPDATE SCFMC.dbo.tbl_charnel SET de_place = '3-301-11308' WHERE %%lockres%% = '1:45744:44'


일단 원본값을 얻기위해서는 데이터가 깨졌기때문에 꽤나 골치아펐다.

그냥 엑셀의 CONCAT과 if else if를 수없이 사용해서 깨진 데이터 사이사이에 없어진 데이터를 낑겨넣어줬다.

=IF($P13255="4-405-18888",$N13255,IF($P13255="4-405-1888",CONCATENATE($N13255,"8"),IF($P13255="4-405-188",CONCATENATE($N13255,"88"),IF($P13255="5-18888",CONCATENATE("4-40",$N13255),IF($P13255="5-1888",CONCATENATE("4-40",$N13255,"8"),IF($P13255="8888",CONCATENATE("4-405-1",$N13255),IF($P13255="4-405-18",CONCATENATE($N13255,"888"),IF($P13255="88",CONCATENATE("4-405-188",$N13255),IF($P13255="888",CONCATENATE("4-405-18",$N13255),IF($P13255="4-405-1",CONCATENATE($N13255,"8888"),IF($P13255="5-188",CONCATENATE("4-40",$N13255,"88"),IF($P13255="5-18",CONCATENATE("4-40",$N13255,"888"),IF($P13255="8",CONCATENATE("4-405-18",$N13255,"88"),"false")))))))))))))

원본데이터를 복원하기 위해 만들었다.

데이터가 깨지지 않았다면 이럴 필요는 없다.


위 함수를 통하여 원본 데이터를 복구하였고


위치 데이터는 먼저 :를 기준으로 Page ID를 스플릿해준다음 16진수를 10진수로 변환하고 Slot ID와 CONCAT함수를 이용하여 조합했다.

https://support.office.com/ko-kr/article/%ED%85%8D%EC%8A%A4%ED%8A%B8-%EB%82%98%EB%88%84%EA%B8%B0-%EB%A7%88%EB%B2%95%EC%82%AC%EB%A1%9C-%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%97%AC%EB%9F%AC-%EC%97%B4%EB%A1%9C-%EB%82%98%EB%88%84%EA%B8%B0-30b14928-5550-41f5-97ca-7a3e9c363ed7




마지막으로 만들은 데이터들을 CONCAT을 통하여 UPDATE문을 생성하였다.

=CONCATENATE("UPDATE SCFMC.dbo.tbl_charnel SET de_place = '",$O13255,"' WHERE %%lockres%% = ","'",$E13255,"'")



그럼 이제 업데이트문을 돌려볼때가 됐다. (테이블을 백업하자)

위에서 추출한 위치데이터는 사건이 발생한 테이블이기 때문에 백업한 테이블에는 작업을 할 수가 없다.

업데이트를 때리고나면 전부다 반영이 되어있을것이다!!


진짜 삭제를 안했기에 필드가 하나라서 다행이었다.

위 작업을 하면서 실력이 많이 늘은것같다.

다음에 주변에서 비슷한 일이 발생하면 잘 도와줄 수 있을 것 같다.

반응형
반응형

일단 aws에서 mysql로 접속해야하는데 aws에서 아래 명령어를 친다.

mysql -u ID -p -h 엔드포인트주소

비밀번호 입력

로그인 성공





pager less -SFX로 쿼리결과 잘나오도록 수정 이후 유저정보 조회

select * from mysql.user;


유저 생성

create user 'ID'@'%' identified by 'PASS'


권한 부여

grant SELECT,INSERT,UPDATE,DELETE,DROP,CREATE on  *.* to 'ID'@'%';

grant *.*로 모든 권한을 주는 명령어도 많이 나와있는데 그러면 access denied뜬다. 그냥 위처럼 쳐주자.

ID에 계정 이름을 넣어주면 된다.


아직은 DB로 다이렉트로 접근 못하기에 ssh 터널로 접속하기위해서 ssh 터널 계정을 파도록 하자.



sudo adduser 계정이름

sudo passwd 계정이름

비밀번호 입력


su 계정이름

비밀번호

로 해당 계정으로 전환후 확인


이후 DB툴에 ssh 터널을 이용하여 로그인 확인해보기


반응형
반응형

가끔가다 무책임한 전임자때문에 아무것도 모르는 서버의 인증서 교체를 해야하는 경우가 있다.

find 명렁어를 쳐서 서버의 모든 cer파일이나 crt파일을 찾으면 그 위치를 보고 확인하면 편하다.

가끔 find 명령어를 치면 access denied때문에 결과값을 못볼때가 있으므로 아래와 같이 명령어로 검색을 한다.

find / -type f -name "*.cer" 2>&1 | grep -v "허가 거부" -> cer파일 찾기

find / -type f -name "*.crt" 2>&1 | grep -v "허가 거부" -> crt파일 찾기


이 방법으로 빠른 교체가 가능했다.

반응형

'Linux' 카테고리의 다른 글

nginx 에러 페이지 설정  (0) 2021.05.11
Filezilla 하위 디렉토리 간편하게 생성 tip  (0) 2018.10.26
리눅스 스크린 명령어  (0) 2018.04.22
반응형


반응형
반응형

사실 질문부터가 잘못됐다.

DB에 삽입한 줄바꿈 문자가 띄어쓰기로만 처리가 되는 것이 아니라 DB에는 잘 들어가지만 css의 문제로 인하여 두칸 이상의 띄어쓰기가 한칸의 띄어쓰기로 바뀌어버리는 것이다.


ㄹㅇ 디비쪽에 잘못들어간다고 확신을 해버려서


텍스트를 이스케이프해서

StringEscapeUtils.escapeJava

https://stackoverflow.com/questions/2406121/how-do-i-escape-a-string-in-java


jsp에서 jstl로 이스케이프된 \r\n을 <br>태그로 바꿔주려고 했다.

<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<% pageContext.setAttribute("newLineChar", "\n"); %>

${fn:replace(item.comments, newLineChar, "; ")}

https://stackoverflow.com/questions/58054/how-can-i-replace-newline-characters-using-jsp-and-jstl

근데 위에방식으로하면 한글문자도 escape처리가 되버려서 한글문자가 안나옴


위의 내용을 하느라 한시간 이상을 날려버렸는데 다른분이 10초만에 해결해주고 가셨다.

표출해주는 태그쪽에다가 아래의 스타일을 걸어주면 잘된다.

style="white-space: pre-line;"



스타일을 걸어주면 된다.

개뻘짓.

반응형
반응형

스프링에서 다운로드 요청을 하면 크롬이나 기타 브라우저는 잘 작동하지만 파이어폭스, 사파리에서는 다운로드가 안되는 경우가 있다.


그럴때는 파일 다운로드에 대한 결과 response에 헤더값을 파일로 지정을 해주면 된다.

response.setHeader("Pragma", "public");

response.setHeader("Expires", "0");

response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");

response.setHeader("Content-type", "application-download");

response.setHeader("Content-Disposition", "attachment; filename=" + fvo.getStreFileNm());

response.setHeader("Content-Transfer-Encoding", "binary");

https://stackoverflow.com/questions/8876924/how-to-download-xml-file-from-server-in-ie-using-java/8877983#8877983

반응형
반응형

유니티에서 안드로이드 빌드시 아래와 같은 메시지가 뜨는 경우가 있다.

Required API level 26.

Make sure Android SDK path is writable by editor.



Use Highest Installed는 현재 설치된 sdk 버전중 가장 높은것을 사용하는 것인데

현재 설치되어있는 sdk 버전정보는 아래 경로에서 확인 가능하다.

C:\Program Files (x86)\Android\android-sdk\platforms (윈도우 기준) 여기 있는 버전이 현재 설치되어있는 버전이다.


나같은경우는 android-23이 깔려있었다.


여튼 위의 메시지에서 Update Android SDK를 눌러도 업데이트가 되지 않는다.

그래서 수동으로 업데이트를 하려고 하는데

정작 안드로이드 공식사이트에서는 sdk 26버전 sdk를 제공하는것같지는 않다. 찾아보니 sdk manager에서 설치해야한다고한다.





안드로이드 sdk manager가 설치되어있는 경로로 와서 관리자 권한으로 실행하자(관리자 권한 아니면 설치할때 에러남)


이후 원하는 버전의 sdk를 설치하자 Android SDK Build-tools의 Rev 26을 선택, 아래 Android 8.0.0을 선택했다.


설치가 끝나면 위의 경로에 26이 깔린것을 확인할수있다.

이후 유니티에서 빌드시 위의 메시지가 다시 나타나지않았다.

반응형
반응형

ASP IIS에다가 작업하는데 이미지 조회시 Failed to load resource: the server responded with a status of 999 (No Hacking)  라는 오류와 같이 이미지가 안불러와진다.

하지만 주위 경로의 이미지들은 제대로 나오는데 말이다...

이것저것 찾아봐도 999에러에 대한 메시지는 나오지않아서 그냥 이미지 이름을 바꾸니까 잘됐다.


문제가 있었던 이미지 이름에는 mainmap_34613.gif였는데 이름을 main_34613.gif 와 같이 바꾸니까 에러없이 잘 불러와진다.

아무래도 IIS나 ASP단에서 어떤 필터링이 걸린것같은데 전달받은사항도없고 그냥 위처럼 처리했다.

하지만 대량의 파일이 필터링 되버리면 문제가 커질것같은데 나중에 팀장님이나 차장님께 여쭤봐야겠다.


상사분께 여쭤보니 ISAPI 필터에 자동으로 걸려있는 필터를 지우니까 되더라...






위 2개의 필터를 제거하니까 접근이 잘 된다.

반응형
반응형

아파치단

http://nahosung.tistory.com/107


톰캣단

https://sarc.io/index.php/tomcat/240-tomcat-response-header-server


나같은경우는 아파치단에서 해결해야했음

반응형
반응형
module.exports.version_list = function () {
return new Promise(function (resolve, reject) {
var sql = "SELECT * FROM STATUS_VERSION";
db.connectDatabase().query(sql, function (err, result) {
if (err) console.log(err);
resolve(result);
});
});
}

이런식으로 프로미스를 이용한 쿼리 실행을 정의하였고


version.version_list.then(function (version_doc) {
res.end(JSON.stringify(version_doc[0]));
})

이렇게 실행을 하였으나 업데이트 쿼리나  실행해도 쿼리값이 제대로 반영이 안되는 문제가 있었다.


함수를 호출할때 ()를 안붙여서 그런것이었는데

아무래도 함수에 대한 결과값이 정해져서 그런것같다.

version.version_list().then(function (result) {
console.log(result);
res.render('admin/version_control', {result: result[0]})
})

이렇게 바꾸니까 잘된다.


간단한 문제였지만 쿼리 실행자체는 문제가 없어서 눈치채기가 힘들었다.

반응형