Apache Solr vs Elasticsearch

http://solr-vs-elasticsearch.com/

Apache Solr vs Elasticsearch

The Feature Smackdown


API

Feature Solr 6.2.1 ElasticSearch 5.0
Format XML, CSV, JSON JSON
HTTP REST API
Binary API SolrJ TransportClient, Thrift (through a plugin)
JMX support ES specific stats are exposed through the REST API
Official client libraries Java Java, Groovy, PHP, Ruby, Perl, Python, .NET, JavascriptOfficial list of clients
Community client libraries PHP, Ruby, Perl, Scala, Python, .NET, Javascript, Go, Erlang, Clojure Clojure, Cold Fusion, Erlang, Go, Groovy, Haskell, Java, JavaScript, .NET, OCaml, Perl, PHP, Python, R, Ruby, Scala, Smalltalk, Vert.x Complete list
3rd-party product integration (open-source) Drupal, Magento, Django, ColdFusion, WordPress, OpenCMS, Plone, Typo3, ez Publish, Symfony2, Riak (via Yokozuna) Drupal, Django, Symfony2, WordPress, CouchBase
3rd-party product integration (commercial) DataStax Enterprise Search, Cloudera Search, Hortonworks Data Platform, MapR SearchBlox, Hortonworks Data Platform, MapR etcComplete list
Output JSON, XML, PHP, Python, Ruby, CSV, Velocity, XSLT, native Java JSON, XML/HTML (via plugin)

Infrastructure

Feature Solr 6.2.1 ElasticSearch 5.0
Master-slave replication Only in non-SolrCloud. In SolrCloud, behaves identically to ES. Not an issue because shards are replicated across nodes.
Integrated snapshot and restore Filesystem Filesystem, AWS Cloud Plugin for S3 repositories, HDFS Plugin for Hadoop environments, Azure Cloud Plugin for Azure storage repositories

Scaling Logstash

http://devquixote.com/devops/2014/10/20/scaling-logstash/

 

로그 축소 비율 조정
나는 최근에 운송 집계 (shippingEasy) 에서 개발 집 (dev ops) 모자를 써야만 로그 집계와 운영 분석을 제공하는 ELK 스택을 설치해야했습니다. 즉 Elasticsearch , Logstash &키바 . 우리는 인프라 스트럭처에 상당히 의존하게되었습니다. 이는 에스컬레이션 지원을 통해 개발자가 참여하도록 지시 할 때 상황이 어떻게 진행되고 있는지 파악하고 프로덕션 문제를 파악할 수 있기 때문입니다. 다음은 평균 응답 시간, 유니콘 작업자 및 대기열 크기와 같은 측정 항목을 보여주는 제작 웹 대시 보드의보기입니다.

이것이 원래 설정되었을 때 각 어플리케이션 서버의 Logstash-Forwarder 로 로그 이벤트를 전달한 Logstash로 전달하여 Elasticsearch로 색인을 생성했습니다. 그런 다음 Kibana를 사용하여 로그 이벤트를 시각화 할 수 있습니다. 이것은 다음과 같은 전형적인 (아마도 순진) 설정입니다 :

logstash-forwarder
\
logstash-forwarder > logstash > elasticsearch < kibana
/
logstash-forwarder
Rails 스택에 대한 뷰를 얻으려면 Elasticsearch에서 사용자 정의 패턴이있는 다중 행 및 grok 필터를 사용하여 로그 파일을 구문 분석했습니다. 우리는 각각의 유니콘 프로세스가 자신의 번호가 매겨진 로그 파일에 기록되도록함으로써 서로 인터리브하는 로그 이벤트를 얻었습니다. 잠시 동안은 잘 돌아 갔지만 트래픽이 휴가를 사기 시작하면서 문제가 발생하기 시작했습니다. 잠시 동안 일할 것이지만 사건의 틈새가 키바 나에 나타나기 시작할 것이고, 물방울이 늦어지고 결국 멈출 것입니다.

고맙게도, 우리의 응용 프로그램이 죽어 가고있는 것은 아닙니다. Logstash가있었습니다.우리는 두 가지 문제로 악화되고 서로를 가려 냈습니다.

Logstash는 우리가 전송 한 모든 로그 이벤트를 처리해야한다는 요구를 따라갈 수 없었습니다.
Logstash 1.4.1-2에는 TCP 입력에 버그 가있어 연결하는 클라이언트가 이전 문제로 인해 시간 초과되기 시작하면 연결 누출이 발생합니다.
Logstash의 버전을 연결 블룸 문제를 수정하는 최신 코드로 패치하여 2 번째 문제를 수정했습니다. 이를 정리하면 Logstash 내의 병목 현상을 확인할 수 있습니다.

Logstash는 jRuby로 작성되었으며 내부는 파이프 라인 으로 설명됩니다 . 매개 변수는 구성의 입력 / 필터 / 출력 스탠자에 설정된 작업을 수행하는 입력, 필터 (작업자) 및 출력 스레드에 의해 처리됩니다. 각 영역에는 20 개의 요소를 저장할 수있는 대기열이 있습니다. 스레드는 대기열에서 당겨서 작업을하고 다음 대기열로 보내거나 반복합니다.Logstash는 각 입력에 하나의 스레드를 할당하고, 단일 작업자 스레드와 각 출력에 대해 하나의 스레드를 할당합니다. 이것은 다음과 같이 보입니다.

input source –> input thread filter thread output thread –> output destination
\ / \ /
queue queue
/ \ / \
input source –> input thread filter thread output thread –> output destination
Logstash의 이러한 영역 중 하나가 채워진 것보다 빠르게 대기열에서 가져올 수없는 경우 문제가 발생합니다. Logstash는 시스템이 백업 할 때 입력 내용에 따라 다양한 효과를 제공합니다. 우리의 경우 Logstash-Forwarder 연결 시간 초과 및 후속 연결 재 연결 시도가 누출되었습니다. Logstash가 redis 목록에서 대기열로 당겨지면 대기열이 팽창합니다.

상단 및 java 스레드 덤프 조합을 사용하여 병목 현상이 필터 작업자 스레드에 있음을 알 수있었습니다. 입력 스레드와 출력 스레드는 CPU 사용량이 거의 없었으며 빈 대기열에서 항상 차단되는 것으로 보입니다. 그러나 필터 작업자 스레드는 CPU 코어를 페깅하고있었습니다. Logstash 배포에서 작업자 스레드의 수를 늘릴 수 있습니다.

잘못된. 이전에 언급 한 여러 줄 바꿈 필터링을 기억하십니까? Logstash의 다중 라인 필터는 쓰레드에 안전하지 않으며 사용시에는 오직 하나의 작업자 쓰레드로만 제한됩니다.이제 다중 라인 코덱을 사용하여 다중 라인 이벤트 콜렉션을 Logstash의 입력 영역으로 이동하기 만하면됩니다. 아니, 그 중 하나를 작동하지 않습니다. 다중 선 필터를 사용하면 파일 이름별로 이벤트를 구분하는 데 사용할 수있는 stream_identity 속성을 지정할 수 있습니다. 다중 라인 입력 코덱은 그런 것을 제공하지 않습니다. 이는 레일을 서로 분리 된 여러 줄의 로그 메시지를 유지하는 모든 노력이 창 밖으로 나올 수 있음을 의미합니다.

이제 우리는 뒤로 물러나서 인프라를 재평가해야했습니다. 궁극적으로 다음을 수행하기로 결정했습니다.

다중 회선 이벤트가 응용 프로그램 서버 측에서 롤업됩니까? 이것은 로그를 꼬리로 묶어서 Logstash로 보내야하는 책임입니다. 그런 다음 Logstash에서 다중 라인 필터를 척킹하고 단일 Logstash 프로세스 내에서 필터 작업자를 확장 할 수 있습니다.
테일링 데몬 app-server 측과 Logstash 사이의 중개인으로 재발행 목록을 사용하면 몇 가지 이벤트 내구성을 가질 수 있고 로그 데이터를 통해 여러 대의 시스템에서 여러 개의 Logstash 프로세스로 확장 할 수 있습니다.
Logstash 전달자는 다중 회선 이벤트 롤업이나 redis와의 통신을 지원하지 않으므로 다른 꼬리표 디먼을 찾아야하거나 Logstash 자체를 각 응용 프로그램 서버에 배포해야했습니다. 우리는 자바 의존성을 도입하고 완료해야 할 일에 매우 무거워 보였으므로 후자를하고 싶지 않았습니다.

위의 요구 사항을 모두 지원하는 파이썬으로 작성된 로그 테일링 데몬 인 Beaver를 입력하십시오 . 우리는 그것이 작동하는지 확인하기 위한 간단한 개념 증명을 수행하여 하나의 웹 서버에 배포하여 24 시간 동안 수행 한 방법을 확인한 다음 모든 서버에 적용했습니다.서비스 중단없이 며칠 동안 제대로 작동합니다. 이제 우리의 인프라는 다음과 같습니다.

beaver
\
beaver > redis < logstash > elasticsearch < kibana
/
beaver
Logstash에서 여러 스레드 / 코어를 사용하여 필터 처리를 수행 할 수있는 응용 프로그램 서버에서 Beaver에 대한 다중 라인 롤업 작업을 수행 한 후에도 Logstash 인스턴스 하나만 있으면 충분합니다. 그러나 로그 트래픽 / 크기를 다시 늘려 Logstash를 압도하기 시작하면 우리는 여러 인스턴스로 확장하여 데이터가 다시 시작되도록하는 것이 좋습니다.

beaver logstash
\ / \
beaver > redis < > elasticsearch < kibana
/ \ /
beaver logstash
그것은 Logstash-scale 땅에서 3 – 4 일을 보내었다. 그것은 우리의 응용 프로그램 사용자에게 양질의 경험을 제공하는 데 정말로 도움이되는 놀라운 도구입니다. ELK 스택의 일부로 Splunk 의 80 %가 실제로 무료입니다. 그러나 유료 라이센스가 없으면 소매를 감아 서 이와 같은 경우에 일해야합니다. 다행히도 그 뒤에 큰 커뮤니티가 있으며 웹과 #logstash의 freenode에서 많은 도움을 얻을 수 있습니다.

Kibana로 데이터에 생명력 불어넣기

http://blog.naver.com/PostView.nhn?blogId=tmondev&logNo=220846929773

 

Log처럼 지속적으로 누적되는 데이터를 실시간으로 분석할 때 ELK stack을 많이 사용합니다. ELK Elastic Search, Logstash, Kibana. 이렇게 3개가 모여서 전체를 이루며 앞글자를 따서 ELK라고 부릅니다. Logstash 대신 Fluentd를 사용한 경우엔 EFK 라고 부르기도 하죠. 관련된 내용은 티몬의 개발이야기에서 이미 다룬 적이 있습니다. (참고. Docker와 ELK Stack으로 로그 분석하기 )

Kibana 란?

이 글에서는 Kibana에 대해서, 그리고 Kibana를 통해 데이터를 시각화하는 방법에 대해 가볍게 다뤄보려고 합니다. 먼저 Kibana란 무엇일까요? Elastic에서는 이렇게 설명하고 있습니다.

번역 : kibana는 데이터 강령술

좋습니다. 그렇다면 Kibana는 언제 사용해야 할까요?
만약 Elasticsearch를 저장소로 사용하면서 Kibana를 사용하지 않는다면 당신은 통계 페이지와, 데이터 처리를 위해 was를 띄우고, amCharts, c3와 같은 chart library를 이용하여 UI 코드를 작성해야 할 것입니다. 물론 이 작업이 누군가에게는 오래 걸리지 않는 작업일 수도 있지만 우리 개발자들은 항상 바쁩니다.

하지만 Kibana를 사용한다면 쉽고 간단하게, 심지어는 단 몇 분만에 데이터 시각화를 완성할 수 있습니다! 게다가 Kibana는 오픈소스라는 장점도 있습니다. 이제 Elasticsearch의 데이터 시각화를 위해서 kibana는 선택이 아닌 필수입니다! 

Kibana를 이용하면 이렇게 멋진 데이터 시각화가 가능합니다!
Kibana 설치 및 준비

그럼, Kibana를 설치해 볼까요?
앞서 말씀 드렸지만, KibanaElasticsearch 의 결과를 보여주는 역할을 담당하기 때문에 ElasticsearchKibana가 필요합니다. 참고로 이번 예제에서 사용될 Kibana 버전은 4.1.7입니다. Elasticsearch 1.7 버전을 사용하고 있기 때문에 해당 버전을 사용하였습니다. Kibana 4.5 이상의 버전은 Elasticsearch 2.3 이상의 버전에서 안정적으로 구동됩니다.

Redis 데이터 모델링

출처:https://www.joinc.co.kr/w/man/12/REDIS/DataModeling#s-1.10.

목차

1. 데이터 모델들

1.1. Message Box

메신저 서비스를 개발하고 있다. 메시지를 보냈는데, 수신 대상이 연결하지 않은 상태일 수도 있다. 이 경우에 메시지는 메시지 함(Message Box)에 저장하기로 했다. 유저가 연결하면, 메시지 함에서 메시지를 읽어온다.메시지 함의 크기는 메시지에 대한 정책에 따라 달라질 수 있다. 나는 “모든 메시지는 중요하다.”는 관점에서 기능을 구현하기로 했다. REDIS는 메모리 기반 데이터베이스이다. 무한정 REDIS에 데이터를 쌓을 수는 없는 노릇이다. 그래서 REDIS에는 최근 도착한 메시지 N개를 저장하고, 그 보다 오랜 메시지는 공간 제약이 없는 다른 영역(예컨데 RDBMS)에 저장하기로 했다.Capped List는 REDIS의 LPUSH와 LTRIM를 이용해서 구현하기로 했다.LPUSH를 수행하면, 리스트의 크기를 반환한다. 반환값이 유저에게 허용한 메시지함 크기 보다 크면, TRIM 연산을 한다.

1
2
3
4
5
6
USER_MAX_MSGBOX_SIZE = 100
list_size = LPUSH message.box.user:1 message
if (n = (list_size – USER_MAX_MSGBOX_SIZE)) > 0
LRANGE message.box.user:1 -n -1 # TRIM .
LTRIM message.box.user:1 0 n
end

작동은 하겠는데, 일단 메시지 박스가 가득차고나면 메시지가 들어올 때마다 “가장 오래된 메시지 저장”->”LTRIM” 연산을 해야 하는 문제점이 있다. 이 문제는 메시지 함 크기에 버퍼를 두는 것으로 해결할 수 있겠다. 메시지 함 의 최대 크기기 100이라면 100 * 2 만큼 메시지를 받는다. 100 * 2가 가득 차면, “가장 오래된 100개의 메시지 저장”->”LTRIM” 하면 효율적으로 운용할 수 있겠다.Capped List 기능을 가진 Message Box의 전체 구성이다.유저가 접근하면, 메시지 함에 있는 메시지를 전부 보여주고 TRIM을 수행한다. 메시지 함을 초과해서 다른 저장소에 저장된 메시지들은 더 보기 명령을 이용해서 네비게이션 할 수 있도록 하면 되겠다. 메시지는 휘발성 이므로 클라이언트에 전달된 메시지는 서버에서 무조건 삭제한다.

1.2. Item 별 방문 카운트

item(상품)별 방문 카운트는 관리자에게는 웹 서비스 최적화를 위한 정보를 제공해 준다. 이 정보들을 꾸준히 저장하면, 고객의 동선과 구매 패턴을 파악하는 기초자료로 사용할 수 있다. 유저별로 page 방문 기록을 저장할 수도 있는데, 이 기록을 이용하면 유저의 기호를 기반으로 하는 추천 시스템 개발에 응용할 수 있을 것이다.

1
> INCR item:item-id

이렇게 하면 item 단위로 count를 할 수 있을 테다. 하지만 이런 류의 데이터는 시계열(time series)이 되어야 쓸만한 정보를 뽑아낼 수 있다. 시계열 데이터를 다룰 때는, 시간 해상도를 결정해야 한다. 하루 단위로 데이터를 저장한다면, 주,월,분기(계절별),년 단위의 유용한 정보를 뽑을 수 있겠지만 “출,퇴근,업무시간,식시시간”등 시간대 별 통계를 얻기는 힘들다. 추천시스템을 만들 경우에도 시간정보가 있어야 정밀한 추천이 가능할 거다. 간단한 예로 통닭을 아침 9시에 추천하는 건 좀 이상하지 않겠는가 ?. 통닭은 좀 너무 예상하기 쉽다면. 음악은 어떤가 ? 아침에 일어나서 듣는 음악과 업무시간에 듣는 음악 저녁시간에 듣는 음악에도 장르별 차이를 예상할 수 있다.일간 카운트 저장 예제다.

1
2
3
4
> INCR item.item-0:20141225
> INCR item.item-1:20141225
> INCR item.item-2:20141225
> INCR item.item-0:20141225

이 key들은 하루동안만 count가 되니, 해당 일이 지난 뒤에서는 파일기반 데이터베이스로 옮기면 되겠다.해상도를 시간단위로 높인다고 가정해도, key 갯수가 * 24가 될 뿐 위의 방식과 달라질 것은 없다.

1
2
3
> INCR item.item-0:2014122511
> INCR item.item-1:2014122511
> INCR item.item-2:2014122512

Item 별 방문 카운트를 약간 응용해보자. A회사는 음악 서비스를 운영하고 있는데, 유저에게 음악을 추천하는 서비스를 만들기로 했다. 유저는 관심있는 음악 페이지를 방문한다고 가정할 수 있다. A사는 각 음악에 장르를 태깅한 다음, 유저가 방문할 때마다 장르에 대해서 카운팅을 하기로 했다. 어느 정도 데이터가 모이면, 이 데이터를 근거로 유저가 좋아할 만한 음악을 추천할 수 있을 거다(정밀한 추천 알고리즘을 돌리려면 다른 데이터들드 좀더 필요하겠지만, 단순화 하기로 했다.). 1부터 10까지 열개의 카테고리로 나눴다. 50은 유저 ID다.

1
2
3
> INCR user.category:50.0
> INCR user.category:50.1
> INCR user.category:50.1

하지만 이 방법이 괜찮을지 확신이 서질 않는다. 천만명의 Active한 유저를 관리해야 하면, key만 1억이다. 샤딩을 해서 키를 분산하는 방법도 있겠는데, 어쨋든 키가 많아지는게 불만이다. REDIS는 separate chainning hash를 사용해서 키가 늘어나더라도 성능을 유지할 수 있다. 메모리를 더 사용해야 한다는 점이 맘에 걸리기는 하지만 큰 문제는 없을 것이다.메모리를 아껴보자는 생각에서 SETRANGE를 사용해 보기로 했다. User 마다 10byte의 고정된 공간을 제공하고, 이 공간에 count 하는 방식이다. 유저 ID는 고정된 integer 값이므로 유저의 카운트 정보가 저장된 위치(offset)을 구할 수 있다.

1
OFFSET = USER_ID * 10

이제 장르별로 key를 만들면 된다. 최대 1000명의 유저가 있다고 가정한다면, 1000 * 10 크기의 key를 만들면 된다.

1
2
3
4
> SETRANGE music.category:0 music.category:0 10000 0
> SETRANGE music.category:1 music.category:0 10000 0
> SETRANGE music.category:2 music.category:0 10000 0
….

Ruby 프로그램을 이용해서 테스트를 해보기로 했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
require redis
# 10 .
# 10 99 ?
FIELD_SIZE = 10
class Counter
@redis = nil
def initialize
@redis = Redis.new(:host=>192.168.57.2)
end
def incr args
offset = args[:user_id] * FIELD_SIZE
key = music.category:#{args[:category]}
count = @redis.getrange(key, offset, offset + FIELD_SIZE 1)
@redis.setrange(key, offset, count.to_i+1)
end
end
user_id = 10
counter = Counter.new
counter.incr :user_id=>user_id, :category=>0

시간 복잡도가 O(1)이다. GET, SET 연산의 복잡도도 O(1)이니, 이 정도면 쓸만하지 싶다. string의 최대 크기는 512M 이고 유저 한명이 차지하는 공간이 10byte라면, key 하나에 5천만 정도의 유저 count를 저장할 수 있다.

1.3. API 호출 제한

OpenAPI 서비스를 하다보면, 일정시간에 호출할 수 있는 최대 API 갯수에 제한을 걸어야 할 때가 있다.간단하게 유저 ID와 날짜를 조합해서 key로 만들고, 이 Key 에 대해서 INCR 하는 방법이 있다. INCR 연산 후에 값(count)을 반환한다. 이 반환 값을 허용한 최대 크기와 비교하면 된다.

1
2
> INCR apicall.user01:20141212
“1812”

일 단위로 카운팅을 하니, 사용하지 않는 키는 배치작업을 이용해서 주기적으로 삭제해야 한다는 귀찮음이 있다.EXPIRE와 조합하는 것으로 이 문제를 해결 할 수 있다. EXPIRE한 key에 대해서 set, getset을 적용하면, EXPIRE 값이 사라진다는 걸 알고 있을 것이다. INCL 연산 대해서는 EXPIRE가 사라지지 않는다.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
key = apicall.#{user_id}:counter
#
# expire .
if !@redis.exists key
@redis.incr key
@redis.expire key, 3600 * 24
end
count = @redis.incr key
# , false .
# API .
# TTL Key ,
# api .
if count.to_i > 10000
return false
end
ttl = @redis.ttl(key)
puts key TTL : #{ttl}
return true
end
def call name
puts API CALL : #{name}
end
end
apimgr = OpenAPIManager.new
if apimgr.call? 2
apimgr.call /test
else
puts OpenAPI Call ERROR
end

문제 없이 작동할 거다. 하지만 카운팅 데이터에 따라서 자동으로 삭제할 필요가 있는지는 고민을 할 필요가 있다. 이런 류의 카운팅 정보는 어딘가에 저장해두고 분석할 필요가 있기 때문이다. 분석을 위해서 데이터를 남겨야 한다면, 첫 번째 방법을 사용해야 할 거다.키가 많아지는 경우를 고민해야 할 수도 있겠는데, 일반적으로 “개발자 등록을 마친 유저”에 대해서 API 호출을 허용할 테니, 크게 걱정하지 않아도 될 것 같다.

1.4. Tag 검색

책 판매 사이트에 Tag 기반 검색을 추가하기로 했다. 아래와 같이 책 정보를 만들고, SADD 명령으로 tag 정보를 만들었다.

1
2
3
4
5
6
7
8
9
10
11
> SET book:1 “{‘title’ : ‘Diving into Python’, ‘author’: ‘Mark Pilgrim’}”
> SET book:2 “{‘title’ : ‘Programing Erlang’, ‘author’: ‘Joe Armstrong’}”
> SET book:3 “{‘title’ : ‘Programing in Haskell’, ‘author’: ‘Graham Hutton’}”
> SADD tag:python 1
> SADD tag:erlang 2
> SADD tag:haskell 3
> SADD tag:programming 1 2 3
> SADD tag:computing 1 2 3
> SADD tag:distributedcomputing 2
> SADD tag:FP 2 3

REDIS는 SINTER(교집합), SUNION(합집합), SDIFF(차집합)의 집합연산 명령을 제공한다. 이들 명령을 이용해서 Tag 검색을 수행할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
require redis
redis = Redis.new(:host=>192.168.56.5)
# 0 results
redis.sinter(tag:erlang, tag:haskell) do | book |
end
# 3 results
redis.sinter(tag:programming, tag:computing).each do | book |
puts redis.get(book:#{book})
end
# 2 result
redis.sunion(tag:erlang, tag:haskell).each do | book |
puts redis.get(book:#{book})
end
# 2 result
redis.sdiff(tag:programming, tag:haskell).each do | book |
puts redis.get(book:#{book})
end

1.5. Log aggregation

시스템에서 발생하는 로그들을 중앙에 저장해서 관리하려고 한다.각 노드는 LPUSH로 밀어 넣고, 처리하는 쪽(로컬 파일 혹은 데이터베이스에 쌓는)에서는 BRPOP로 꺼낸다.

1
2
3
4
5
6
7
require ‘redis’
redis = Redis.new(:host=>’192.168.56.5′, :port=>6379)
loop do
item = redis.brpop(‘logging’, 0)
puts item[1]
end

REDIS PUB/SUB로도 구현할 수 있지 않을까 ? 라는 생각을 해봤지만 Subscribe가 뻗어버려서 메시지를 소비할 녀석이 없을 경우 메시지를 버려버리는 문제 때문에 사용하기 힘들 것 같다.

1.6. Pub/Sub Communication

Pub/Sub 시스템으로도 사용할 수 있다.

1
2
> SUBSCRIBE log
> PUBLISH log Hellow

PUB 메시지는 큐등에 쌓이지 않는다. Subscriber가 없다면, 메시지를 잃어 버릴 수 있다는 의미다. 따라서 PUB/SUB 시스템은 분실되도 큰 상관 없는 곳에 사용해야 한다. 예를 들어 서버 점검을 클라이언트에게 알려주기 위한 알람 용도로의 사용이다. WoW(World of warcraft)의 경우 서버 점검 1시간 전 10 분전 5분전에 알람 메시지를 줘서 유저가 미리 대비할 수 있게 한다.

1.7. shopping cart 관리

쇼핑 카트를 관리해 보자. 이 서비스의 기능요소들은 아래와 같다.

  1. 유저는 쇼핑 카트를 만들 수 있다.
  2. 쇼핑카트에는 하나 이상의 상품을 담을 수 있다.
  3. 상품을 빼는 것도 가능하다.
  4. 상품은 하나 이상 담을 수 있다.

카트를 식별할 ID를 가져야 할 건데, 유저 ID로 만들기로 했다. 유저는 하나의 카트만 가질 수 있다는 제한이 걸리는데, 별 상관 없을 것 같다.카트에 담은 상품관리

UserID ProductID Qty
1 28 1
1 372 2
2 15 1
2 160 5
2 201 7

UserID를 Key로 한 값에 ProductID:Qty 형식의 key/value의 리스트를 저장해야 한다. REDIS의 Hash를 이용하면 되겠다.카트 테이블 정보를 Redis에 밀어 넣었다.

1
2
3
4
5
> HSET cart.user:1 28 1
> HSET cart.user:1 372 2
> HSET cart.user:2 15 1
> HSET cart.user:2 160 5
> HSET cart.user:2 201 7

테스트

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class ShoppingChart
@redis = nil
def initialize
@redis = Redis.new(:host=>192.168.56.5, :port=>6379)
end
def allItem userid
puts HGET ALL ITEM ====
puts %10s : %s % [Product,Qty]
@redis.hgetall(cart.user:1).each do | field, value |
puts %10s : %s % [field, value]
end
end
def removeItem userid, productId
@redis.hdel cart.user:#{userid}, productId
end
def addItem userid, productId, qty
@redis.hset cart.user:#{userid}, productId, qty
end
end
cart = ShoppingChart.new
cart.allItem 1
cart.removeItem 1, 28
cart.allItem 1
cart.addItem 1, 1280, 5
cart.addItem 1, 1312, 2
cart.allItem 1

웹 애플리케이션의 경우 유저 세션이 만료되는 시점을 알 수 없다. 다음번 방문했을 때, 이전 카트가 보인다거나 하는 문제도 있고, 메모리를 낭비하는 문제도 있으니, 카트 만료시간을 정해줘야 한다. 간단하게 EXPIRE를 이용해서 만료시간을 정하자. 카트 메서드를 호출 할 때마다, 만료시간을 재설정 하면 되겠다.

1.8. Atomic Get and Delete

GET과 DELETE를 원자적으로(atomically) 수행하고 싶다면, MULTI-EXEC 명령을 이용하면 되겠다.

1
2
3
4
5
6
7
8
9
> SET toto 1
> MULTI
> GET toto
QUEUED
> DEL toto
QUEUED
> EXEC
1) “1”
2) (integer) 1

1.9. Simple social graph

친구의 친구의 친구의 친구의 친구.. 관계를 그래프로 표현하면 소셜 그래프라고 된다고 하더라. 이 관계는 follows와 followers, block의 리스트로 정의 할 수 있다. 아래와 같은 소셜 그래프를 REDIS에 저장해 보자.유저 “1”을 중심으로 소셜 그래프를 만들었다. 화살표는 팔로잉의 방향이다. 1과 2 관계에서 2는 1의 팔로워다.(2는 1을 팔로잉 하고 있다.) 1과 3은 서로 팔로잉한 “친구” 관계다. 4와 5는 아는 사람 정도가 되겠다. 4는 아는 사람이 둘이고 5는 아는 사람이 한명이다. SET을 이용해서 데이터를 구성했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1 2
> SADD user.follower:1 2
> SADD user.following:2 1
# 1 3
> SADD user.friend:1 3
> SADD user.friend:3 1
# 3 4
> SADD user.following:3 4
> SADD user.follower:4 3
# 2 4
> SADD user.following:2 4
> SADD user.follower:4 2
# 3 5
> SADD user.following:3 5
> SADD user.follower:5 3

테스트 결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 4
> SMEMBERS user.follower:4
1) “2”
2) “3”
# 1
> SMEMBERS user.follower:1
1) “2”
# 1
> SMEMBERS user.friend:1
1) “3”
# 1 3 , 3
# , , .
> MULTI
> SMEMBERS user.follower:3
QUEUED
> SMEMBERS user.following:3
QUEUED
> SMEMBERS user.friend:3
> EXEC
1) (empty list or set)
2) 1) “4”
2) “5”
3) 1) “1”

이 데이터 모델은 유저간의 친밀도(weight – 가중치)를 알 수 없다는 문제가 있다. 소셜 네트워크 기반의 서비스를 하려면 유저간의 친밀도 값을 함께 가지고 있어야 한다. 가중치를 적용한 관계 그래프는 아래와 같을 것이다.유저 1에게 다른 유저를 추천한다고 가정해 보자. 유력한 대상은 4, 5, 6 이다. 가중치가 없다면 무작위로 추천을 해야 겠지만, 가중치가 있다면 어떤 유저를 우선 추천해야 하는지 계산할 수 있을 거다.유저간 각 관계에는 숫자로 가중치가 있는데, 경로에 있는 가중치를 모두 더해서 값이 큰 녀석을 추천하는 알고리즘을 만들기로 했다. 친구의 경우 쌍방향인데, 이 값을 모두 더하기로 했다.(친한 친구의 아는 사람이라면 당연히 가중치가 더 높을 것이다.)

  • 1 에서 5 : (3 + 3) + 4 = 10
  • 1 에서 4 : 2 + 1 + ( 3 + 3 ) + 2 = 11. 4는 2와 3 모두가 알고 있다. 따라서 모든 경로의 값을 더해서 가중치를 높게 잡았다.
  • 1 에서 6 : 2 + 1 = 3

이 알고리즘에 따르면 4, 5, 6 순서로 추천을 하면 된다는 결과가 나왔다. 실제로는 경로를 구성하는 Hop의 갯수를 포함하는 알고리즘을 개발해야 겠지만, 여기에서는 그냥 단순한 알고리즘을 사용한다. (소셜 그래프에서 추천 알고리즘은 연구해 볼만한 가치가 있겠다.)이제 가중치를 어떻게 저장할 것이냐 하는 이슈가 있다. 가중치를 별도의 키로 저장하는 방법을 생각해 볼 수 있겠다. 유저 1을 중심으로 할 경우 아래와 같이 저장한다.

1
2
3
> SADD route.weight:1.2 2
> SADD route.weight:1.3 6
> SADD route.weight:1.5 5

간단하긴 하지만, 유저가 많아지고 관계가 촘촘해 질 수록 데이터의 양이 기하 급수적으로 늘어난 다는 단점이 있다. 그냥 관계 그래프를 만들 때, 값에 가중치를 포함하면 된다.

1
2
3
4
5
6
7
# 1 2
> SADD user.follower:1 “[2, 2]”
> SADD user.following:2 “[1, 2]”
# 1 3
> SADD user.friend:1 “[3, 6]”
> SADD user.friend:3 “[1, 6]”

소수점을 이용해서 가중치를 저장하는 방법도 있다.

1
2
3
4
5
6
7
# 1 2
> SADD user.follower:1 2.2
> SADD user.following:2 1.2
# 1 3
> SADD user.friend:1 3.6
> SADD user.friend:3 1.6

가중치가 1을 넘지만 않게 조절한다면, 첫 번째 방법보다 효율적으로 동작할 거다. 가중치외에 다른 부가적인 정보를 넣고 싶다면 첫번째 방법을 사용하면 된다.ZADD를 이용해서 구현 할 수도 있다. ZADD는 “정렬을 위한 추가적인 값이 필요” 하다는 것을 제외하면 SADD와 동일하다. ZADD로 데이터를 다시 만들어봤다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1 2
> ZADD user.follower:1 2 2
> ZADD user.following:2 2 1
# 1 3
> ZADD user.friend:1 6 3
> ZADD user.friend:3 6 1
# 3 4
> ZADD user.following:3 2 4
> ZADD user.follower:4 2 3
# 2 4
> ZADD user.following:2 1 4
> ZADD user.follower:4 1 2
# 3 5
> ZADD user.following:3 4 5
> ZADD user.follower:5 4 3

테스트 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
require redis
class Friend
@id = nil
@redis = nil
def initialize id
@redis = Redis.new(:host=>192.168.56.5)
@id = id
end
def follower
@redis.zrange user.follower:#{@id}, 0, -1, :with_scores => true
end
def following
@redis.zrange user.following:#{@id}, 0, -1, :with_scores => true
end
def friend
@redis.zrange user.friend:#{@id}, 0, -1, :with_scores => true
end
end
id = ARGV[0]
my = Friend.new id
my.follower.each do | v |
puts #{v[0]} : #{v[1]}
end
my.following.each do | v |
puts #{v[0]} : #{v[1]}

가중치는 아래의 서비스에 이용할 수 있을 거다.

  • 이웃(친구의 친구) 추천 : 높은 가중치를 가지는 친구의 이웃을 추천하는게 적중도가 높겠다.
  • 컨텐츠 추천 : 가중치가 높은 친구는 컨텐츠에 대한 기호가 비슷할 확률이 높다. 컨텐츠 소비 데이터와 함께 사용한다면, 정교한 추천이 가능할거다.

이 예제에서 가중치는 follower, following 두 개 요소로만 계산하고 있다. Like, 메시지 전송등과 같은 요소들을 이용해서 정교한 가중치 모델을 만들어 보는 것도 재미있겠다.소셜 그래프의 응용은 따로 문서를 만들어서 고민해봐야 겠다.

1.10. FIFO Queue

REDIS는 List 데이터타입을 지원한다. LPUSH와 RPOP를 이용해서 LIST 데이터에 대한 큐를 만들 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
> LPUSH queue1 low
(integer) 1
> LPUSH queue1 medium
(integer) 2
> LPUSH queue1 high
(integer) 3
> RPOP queue1
“low”
> RPOP queue1
“medium”
> RPOP queue1
“high”

REDIS는 RPUSH, LPUSH, LPOP, RPOP를 이용해서 데이터를 넣고 뺄 수 있다. 또한 blocking pop도 지원한다. 이들 연산의 시간 복잡도는 모두 O(1)로, 자료의 크기에 상관없이 동일하게 빠른 성능을 뽑아낼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
require redis
class Queue
@id
@key
def initialize
@redis = Redis.new(:host=>192.168.56.5)
id = @redis.incr(queue_space)
@key = queue:#{id}
end
def push v
@redis.lpush(@key, v)
end
def pop
@redis.rpop(@key)
end
def key
@key
end
end
queue = Queue.new
queue.push Job 1
queue.push Job 2
queue.push Job 3
puts queue.pop

1.11. Session Storage

HTTP 기반의 모든 웹 애플리케이션들은 session을 이용해서 상태를 관리한다. 세션정보는 데이터베이스 혹은 공유파일 시스템을 이용해서, 모든 웹(혹은 WAS)서버들이 공유해야 한다. REDIS를 이용해서 세션 저장소를 만들어 보려고 한다.세션 저장소는 아래의 기능을 가지고 있어야 한다.

  1. Create : 세션을 만든다.
  2. Destroy : 세션을 삭제한다.
  3. Read : 세션에 저장된 정보를 읽는다.
  4. Write : 세션에 데이터를 쓴다.
  5. Gc : 사용하지 않는 세션은 자동으로 정리해 줘야 한다.

세션이름을 Key로 하고 데이터로 string을 저장한다. Gc는 EXPIRE 명령으로 구현한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
require redis
class Session
@r = nil
@ttl = nil
def initialize ttl
@r = Redis.new(:host=>192.168.56.5)
@ttl = ttl
end
def create
r = Random.new
id = r.rand(0..10000)
session = session:#{id}
@r.expire session, @ttl
@r.set session, {‘id’:’#{id}‘}
return session
end
def read session_id
if @r.exists session_id
@r.expire session_id, @ttl
return @r.get(session_id)
end
end
def write session_id, data
if @r.exists session_id
@r.expire session_id, @ttl
return @r.set(session_id, data)
end

1.12. Auto Complete

영어사전 서비스에 자동완성 기능을 제공하려고 한다. 나는 단어들을 계층(Tree)적으로 구성하기로 했다.단어의 경로를 단지 계층적으로만 구성하면, 수많은 단어들 중에서 적절한 단어를 추천할 수 없을 것이다. 자동완성 서비스의 품질을 높이기 위해서는 적절한 단어를 추천하기 위한 알고리즘이 필요하다. 그래서 각 경로에 가중치(weight)를 주기로 했다. 이 알고리즘은 아래와 같이 작동한다.유저가 단어를 선택했다면, 단어의 경로를 계산할 수 있다. 위 그림에서 유저가 “apple”를 선택했다면, 경로는 a -> ap -> app -> apple 이 된다. 그러면 모든 경로에 대해서 가중치를 +1 한다. 이제 유저가 “a”를 선택하면, 가중치가 높은 “ap”가 높은 우선순위로 추천될 것이다. 유저가 입력하는 모든 단어에 대해서 이 연산을 수행하면 된다. (물론 유저마다 관심분야가 다르기 때문에, 제대로 서비스하려면 개인화 해야 한다. 고수준 응용은 따로 고민해서 정리해야 겠다. 여기에서는 모델을 단순화 한다.)

1
2
3
4
5
6
7
8
9
10
ZADD term.next:a 0 ac
ZADD term.next:a 0 ap
ZADD term.next:a 0 agree
ZADD term.next:ap 0 app
ZADD term.next:ap 0 apoint
ZADD term.next:app 0 apply
ZADD term.next:app 0 apple
ZADD term.next:app 0 application

유저가 apple를 입력하면, a와의 경로에 있는 모든 단어에 대해서 가중치를 높여야 한다. 단어가 선택될 때마다 경로를 찾는 건 너무 비효율적이라고 생각해서, LIST에 정리하기로 했다.

1
2
3
4
5
6
LPUSH term.route:apple apple app ap a
LPUSH term.route:apply apply app ap a
LPUSH term.route:application application app ap a
LPUSH term.route:apoint apoint ap a
LPUSH term.route:ac ac a
LPUSH term.route:agree agree a

app나 ap 등의 경로는 apple, apply, application에서 이미 만들어졌다. 따라서 굳이 경로 정보를 추가할 필요 없이 SCAN 명령으로 찾을 수 있다.

1
2
3
4
5
6
7
8
> SCAN 0 MATCH term.route:app* COUNT 3
1) “0”
2) 1) “term.route:apple”
> LRANGE term.route:apple 0 -1
1) “a”
2) “ap”
3) “app”

테스트 코드

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# term , .
# zrevrange score .
def next term
@redis.zrevrange term.next:#{term}, 0, -1
end
# term .
def route term
@redis.lrange term.route:#{term}, 0, -1
end
# term submit ,
# +1 .
def addWeight term
i = 0
items = route(term)
items.each do | v |
if items[i+1] != nil
@redis.zincrby term.next:#{v}, 1, items[i+1]
i = i+1
end
end
end
end
ac = AutoComplete.new
ac.addWeight apple
puts ac.next a
  1. 유저가 “apple” 단어를 입력했다.
  2. addWeight “apple” 메서드가 호출 된다.
  3. 이제 a <-> ap <-> app <-> apple 는 모두 +1의 가중치가 적용된다.
  4. 유저가 “a”를 입력하면, ap, ac, agree를 추천한다. 추천은 내림차순으로 정리되므로 ap가 가장 높은 우선순위로 추천된다.
  5. 유저가 “ap”를 입력하면, app이 높은 우선순위로 추천된다.
  6. app를 입력하면 apple를 추천한다

구글 검색 서비스의 검색어 추천을 보면, 두 단계 혹은 세 단계로 확장해서 검색어를 추천하는 걸 볼 수 있다. 예를 들어 “a”를 입력했을 때, 위의 코드는 “ap, ac, agree”에서만 추천하지만, 구글의 경우에는 “a, ac, ap, agree, app, apoint”등으로 범위를 넓혀서 추천한다.2단계까지 범위를 넓히면 a를 입력했을 때 “ap > app > ac > agree == apoint” 순으로 추천이 가능하다. 단어 추천 품질이 더 높아질 거다. (성능도 고려해야 하기 때문에)구현이 복잡해 지는게 단점이다. 고민해 볼만한 재미있는 주제이지만, 여기에서는 다루지 않는다.

1.13. 검색어 추천

Auto Complete와 비슷하지만 다른 점이 있다. 위의 auto complete 데이터 구조는 “한 단어”를 대상으로 한다. 반면 검색어의 경우 두 개 이상의 단어로 이루어지는 경우가 있는데, 여기에는 적용하기가 힘들다. 단순하게 구현하자면 각 단어에 대해서 auto complete 알고리즘을 적용하면 되겠지만, 서비스 품질을 보장하기 힘들 거다. 서비스 품질을 보장하려면 “단어와 단어 사이에도 가중치”를 둬야 한다.예를 들어 “Linux”라는 단어에 대해서는 “Apple” 보다는 “Torvalds”가 더 높은 가중치를 가질 것이니, Torvalds를 추천하는게 합리적이다…. 정리 중

2. 기타 고려해야 할 것들

2.1. 메시지 정리

Log aggregation처럼 중앙에 메시지를 수집하기 위해서 REDIS를 사용하는 경우, REDIS 테이터베이스가 꽉 차는 경우에 대한 대비가 필요하다.REDIS가 꽉 찰 경우 capped list하는 방법이 있겠으나 상당히 위험한 방법이다. REDIS 인스턴스의 메모리 혹은 list/key의 갯수를 모니터링 해서, 임계치를 초과 할 경우 관리자에게 알려줘서 처리하도록 하는게 안전할 것 같다.Info를 이용하면 keyspace에서 각 데이터베이스별 key 크기를 확인할 수 있다.

1
2
3
4
5
6
used_cpu_sys_children:0.00
used_cpu_user_children:0.00
# Keyspace
db0:keys=10,expires=1,avg_ttl=45761
db1:keys=20,expires=0,avg_ttl=45761
  • keys : expires 설정된 키를 포함한 모든 키의 갯수
  • expires : expires 설정된 키.

Expires는 keys중에서 expires가 설정된 키의 갯수를 의미한다. 예를 들어 db0의 keys는 10개다. Expires 설정된 키가 삭제되면 keys는 9가 될 것이다.dbsize로 가져올 수도 있다. info처럼 상세한 정보를 출력하지는 않으며, select한 db의 정보만 가져온다.

1
2
3
4
5
6
> select 1
> dbsize
(integer) 20
> select 0
> dbsize
(integer) 10

리스트 크기는 LLEN으로 가져올 수 있다.이들 정보를 수집했다면, 임계치를 정해서 alert 메시지를 발생하도록 설정한다. 임계치의 70%는 일반 경고 메시지, 90%는 크리티컬 경고 메시지를 발생하게 해서 적절한 조치(POP하는 인스턴스가 제대로 작동하는지 확인 혹은 POP 하는 인스턴스를 늘리는 등의)를 취하면 된다. 나는 zabbix를 이용해서 모니터링 시스템을 구축했다.

3. 클라이언트 연결 테이블 관리

4. 참고

  • http://www.scribd.com/doc/33531219/Redis-Presentation
  • http://oldblog.antirez.com/post/autocomplete-with-redis.html
  • http://www.slideshare.net/jinojjan/node-js-redis
  • http://www.slideshare.net/Byungwook/redis-data-modeling-examples
  • http://openmymind.net/Data-Modeling-In-Redis/

Redis GUI Tool – Redis Desktop Manager 설치

소스코드  : https://redisdesktop.com/download

다운받아 설치 하였지만 에러가 난다.

https://aur.archlinux.org/packages/redis-desktop-manager-bin/

에서

https://github.com/uglide/RedisDesktopManager/releases/download/0.9.0-alpha4/redis-desktop-manager_0.9.0.17_amd64.deb

를 다운받아 설치

의존성 문제는

sudo apt-get -f install 로 해결

 

 

 

redis redis-trib.rb  에러 발생시

redis 실행 해보니 아래 메세지가 발생시 패키치를 더 설치해줬다.
$ ./redis-trib.rb
/usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require’: cannot load such file — redis (LoadError)
from /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require’
from ./redis-trib.rb:25:in `<main>’
$ yum install ruby ruby-devel rubygems rpm-build
$ gem install redis

RHEL/CentOS 에 redis 설치

  • 사전 준비
  • 설치
  • 같이 보기

 

Open Source Key/Value store 인 redis 의 최신 버전을 yum 으로 설치하는 방법.

 

사전 준비

  1. Remi 저장소에서 redis 최신 버전을 배포하며 redis는 EPEL 저장소에 있는 jemalloc 패키지를 필요로 하므로 EPEL과 Remi 저장소를 설치해야 한다.
    CentOS 6
    rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
    rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm

    RHEL/CentOS 7 은 RHEL/CentOS 5,6,7 에 EPEL 과 Remi/WebTatic Repository 설치하기 참고하여 저장소 설치

 

설치

  1. redis 설치 (epel, remi 저장소는 기본적으로  활성화되지 않으므로 –enablerepo 옵션을 주거나 /etc/yum.repos.d/{remi,epel}.repo 파일내 enabled 항목을 1로 설정해야 한다.)
    yum --enablerepo=epel,remi install redis
  2. 부팅시 자동 실행하도록 설정
    서비스 자동 실행 설정
    ## CentOS 6
    chkconfig redis on
     
    ## CentOS 7
    systemctl enable redis.service
  3. redis 구동
    ## CentOS 6
    service redis restart
     
    ## CentOS 7
    systemctl restart  redis.service

같이 보기

  • PHP 로 redis 연계 하기

RHEL/CentOS 에서 PHP 로 redis 연계 하기

  • 설치
    • source compile
    • yum 으로 설치
  • 확인
  • SELinux 설정
    • CentOS 6
    • CentOS 7
  • 같이 보기
  • Ref

 

PHP 에서 redis 에 연동하기 위한 client library 중 가장 유명한 것은 Pure PHP 로 개발한 predis 와 C 언어로 만든 PHP 모듈인 phpredis 두 가지이다.

두 가지중 C 로 만든 phpredis 가 성능이 더 우수하다는 벤치마크 결과가 많으므로 이를 사용하여 PHP 에서 redis 에 연계하는 방법을 알아 보자.

 

설치

PHP 를 remi 또는 webtatic 어디에서 설치했는지와 PHP 버전에 따라서 PHP 관련 패키지 명이 약간 다르다.

remi 저장소의 PHP 패키지는 php54, php55 처럼 php 뒤에 버전이 붙고 WebTatic 은 php54w, php55w 처럼 php 버전뒤에 w 가 붙는다.

source compile

  1. 소스를 컴파일해서 설치하려면 먼저 gcc 컴파일러와 php 개발 패키지와 igbinary serializer(옵션)를 설치해야 한다. ()
    # Webtatic 에서 php 5.4 를 설치했을 경우
    yum install gcc php54w-devel php54w-pecl-igbinary
     
    ## Remi 에서 php 5.5 를 설치했을 경우
    yum install gcc php55-devel php55-pecl-igbinary
  2. github 에서 소스 복제
    git clone https://github.com/phpredis/phpredis
    cd phpredis
  3. autoconf 및 Makefile 생성.  –enable-redis-igbinary 는 igbinary 패키지를 설치했을 경우에만 사용
    phpize
    ./configure --enable-redis-igbinary
  4. 컴파일
    make
  5. 설치 (root 로 실행)
    make install
    echo "extension=redis.so" > /etc/php.d/redis.ini

 

yum 으로 설치

 

PHP 설치 저장소에 따라 패키지명이 약간 다르다.

# webtatic 저장소에서 PHP-5.4를 설치할 경우
yum install php54w-pecl-redis --enablerepo=webtatic
 
## PHP 5.5
yum install php55w-pecl-redis --enablerepo=webtatic

 

확인

  1. phpinfo(); 를 호출하여 extension 에 redis 가 있는지 확인
  2. 다음 php 코드 수행
redis.php
<?php
$redis = new Redis();
try {
    $redis->connect('127.0.0.1','6379', 2.5, NULL, 150);
    $key = 'myKey';
    $value = array('val1' => 'myValue1',
                    'val2' => 'Value2');
    $ttl = 3600;
    $redis->setex( $key, $ttl, $value );
    $value = $redis->get($key);
    var_dump($value);
} catch(RedisException $e) {
    var_dump($e);
}
$redis->close();
?>

 

SELinux 설정

CentOS 6

위 예제 코드를 쉘 상에서 php redis.php 로 실행하면 정상 동작하지만 웹 서버에서 실행하면 다음과 같은 에러가 발생하고 정상적으로 동작하지 않는다.

PHP Fatal error:  Uncaught exception ‘RedisException’ with message ‘Redis server went away’ in /var/www/html/redis.php:20

이는 PHP 는 웹 서버에서 실행되므로 웹 서버의 권한을 상속받는데 SELinux 보안 정책에 의거해 웹 서버는 redis 가 사용하는 포트인 6379 에 연결할 수 없기 때문이다.

semanage 명령어로 httpd 가 6379 포트에 연결할 수 있도록 설정해 주면 SELinux enforce mode 에서도 정상 동작한다.

semanage port -a -p tcp -t http_port_t 6379

CentOS 7

CentOS 은 redis_port_t 라는 context 가 추가되었으므로 semanage port -a 명령어를 실행하면 오류가 발생한다. 다음 custom TE 를 작성한 후에 module 을 컴파일하고 로딩하면 정상 동작한다.

  1.  httpd-redis.te  파일 작성
    httpd-redis.te
    module httpd-redis 1.0;
    require {
            type redis_port_t;
            type httpd_t;
            class tcp_socket name_connect;
    }
    #============= httpd_t ==============
    #!!!! This avc can be allowed using the boolean 'httpd_can_network_connect'
    allow httpd_t redis_port_t:tcp_socket name_connect;
  2. audit2allow 로 TE 컴파일
    audit2allow -a -M httpd-redis
  3. module 설치
    semodule -i httpd-redis.pp

 

같이 보기

 

Ref

에틸카바메이트(Ethyl carbamate)

http://www.seehint.com/hint.asp?no=11542

에틸카바메이트(Ethyl carbamate)  

에틸카바이트
카바이트
에틸렌

에틸카바메이트는 여러 가지 발효식품에서 발견되는 자연 발생적인 발암성 물질
독성은 이 물질이 대사되어 활성형태로 될 가능성은 활성화 정도와 해독 시스템에 따라 달라진다

에틸카바메이트는 우레탄(Urethane)으로도 알려져 있으며, 예전에는 동물의 마취제와 사람의 항종양 및 수면제로 널리 사용되었으나 1943년 쥐에서 폐종양의 형성을 유도하는 요인을 조사하면서 발암성이 알려지기 시작하였다. 1980년대부터 이 물질에 대한 연구를 시작하여, 이 물질은 암모니아와 에틸클로로포르메이트의 반응으로 생성되며, 식품에서는 주로 요소(Urea)와 에탄올을 가열할 때 생성된다는 점이 밝혀졌다. 그러므로 대체적으로 발효식품에서 많이 발견된다. 1990년대에 와서 빵(2 ppb), 간장(20 ppb)에서도 발견되었고, 에틸카바메이트 생성반응이 높은 온도에서 잘 일어나므로 강화와인이나 위스키 등에서 더 많이 검출된다. 이에 따라 1988년 알코올음료 생산업자들이 자율적으로 그 함량을 규제하여, 와인은 15 ppb이하, 증류주는 125 ppb 이하로 그 기준을 정하였다.

ClCOOCH2CH3 + 2NH3 → NH2COOCH2CH3 + NH4Cl
ethyl chloroformate          ethyl carbamate

CH3CH2OH + NH2CONH2 → NH2COOCH2CH3 + NH3
ethanol         urea               ethyl carbamate

종양형성의 메커니즘
인간이 섭취하는 식품에는 여러 가지 천연 발암제와 돌연변이 유발제가 들어있다. 이러한 물질들은 여러 단계에서 종양 형성을 유도하는데, 종양이 생기려면, 첫째는 섭취한 물질이 생물학적 활성을 가진 형태로 전환되어야 하며, 둘째는 활성형태의 물질이 DNA의 뉴클레오타이드 염기와 반응하여 염기를 전환 혹은 수정해야 한다. 그리고 변형된 염기는 세포내 DNA 복구 메커니즘에 의한 검색이나 수정에서 벗어나야 가능하다. 그러니까 암에 걸리려면 이 세 가지 단계를 거쳐야 가능하다. 생체 내로 흡수된 에틸카바메이트의 4-6 %는 변형되지 않은 채 오줌으로 배설괴고, 대부분(90-95%)은 간에서 에스테라제(Esterase)에 의해서 가수분해되어 에탄올, 암모니아, 탄산가스가 된다. 나머지 1 %가 문제를 일으키는데, 이 해독능력과 그 비율은 개인의 유전자에 따라서 달라진다.

에틸카바메이트의 규제
에틸카바메이트는 살균제(Diethylpyrocarbonate)로 처리한 과일에서 처음으로 검출되었으며, 이어서 이 살균제를 사용하지 않은 채소류에서도 자연적으로 생긴다는 것을 발견했다. 1985년 캐나다 온타리오 주류통제국은 정상보다 이 물질의 농도가 높은 캐나다의 와인과 증류주를 거부하면서 식품과 음료의 에틸카바메이트 문제를 제기하였고, 뒤에 미국연방정부도 알코올음료와 다른 식품의 에틸카바메이트 농도를 조사하여, FDA는 각 해당산업분야에서 제품 중 에틸카바메이트 실험방법을 발전시키고 그것을 감소시키는 방법을 찾을 것을 요구하고 있다.

다른 식품의 에틸카바메이트
몇 가지 연구에서 여러 가지 식음료의 에틸카바메이트의 농도를 분석하여 발표했다. 한 학자(Vahl)는 덴마크 식품 중, 빵에서 0.8-12 ppb, 요구르트에서 흔적-6.6 ppb, 테이블와인은 흔적-29 ppb, 포트와 셰리 등 디저트와인은 더 높은 7-61 ppb, 증류주인 럼은 0.5-25 ppb, 브랜디는 63-5,103 ppb이 들어 있다고 발표한 적이 있다. 이 수치는 전에 발표된 것과 완벽하게 일치한다. 또 다른 학자(Canas)들은 치즈에서 흔적-6 ppb, 간장은 46 ppb 이상, 청주에는 10-904 ppb 정도 있다고 밝혔다. 이 측정치를 기초로 하여 여러 가지 식품의 소비수준을 참작한다면 서양 사람에게 에틸카바메이트의 주요 공급원은 빵이라고 할 수 있다.

※ 알코올음료의 에틸카바메이트 함량(FDA 자료)
브랜디: 10-45 ppb
위스키: 55-70 ppb
럼: 2-5 ppb
리퀴르: 10-25 ppb
셰리: 10-40 ppb
포트: 23-26 ppb
와인: 10-15
사케: 55-60 ppb

매실주 담글 때 100일 넘기지 마세요.  흠 없는 매실, 도수 낮은 술 써야.
[중앙일보] 입력 2011.06.22
매실주에서 발암 추정물질인 에틸카르바메이트가 검출됐다. 매실이 많이 들어가고 알코올 도수가 높을수록 에틸카르바메이트의 검출량도 많았다. 한국소비자원은 21일 시 판 매실주와 가정에서 담근 매실주 33종을 수거해 분석한 결과 총 28종의 매실주에서 11~379ppb의 에틸카르바메이트가 검출됐다고 밝혔다. 국내에는 에틸카르바메이트 허용 기준이 없지만 캐나다·체코에선 와인은 30ppb 이하, 과실주는 400ppb 이하로 정하고 있다. 에틸카르바메이트는 국제암연구기관(IARC)에서 인체에 암을 발생시킬 수 있는 ‘2A그룹’으로 분류하고 있다. 동물 실험에서 암을 유발하는 것으로 확인됐으나 인체에 서는 확인되지 않은 물질이란 뜻이다. 매실주에서 검출되는 에틸카르바메이트는 매실의 씨에 있는 ‘시안배당체’란 독성물질이 알코올과 반응해 만들어진다. 술의 도수가 높고, 매실의 양이 많을수록 에틸카르바메이트 검출량이 많은 건 이 때문이다.
한국소비자원 측은 “오래 묵힐수록 좋은 과실주라는 인식 때문에 매실을 담근 채 술을 장기간 보관하는 경우가 많은데 그럴수록 발암 추정물질이 더 많이 생성된다”고 경고했다. 또 매실주를 담글 때는 ▶술은 도수가 낮고, 매실은 상하지 않은 것을 쓰고 ▶매실을 담그는 기간은 100일을 넘기지 않되 ▶햇볕이 들지 않는 건조하고 서늘한 곳에 보관하라고 당부했다.  한국소비자원 관계자는 “이번 조사에서 검출된 에틸카르바메이트의 양은 위험한 수준은 아니다. 하지만 매실주는 많은 사람이 즐겨 마시는 술인 만큼 식약청에 관리기준을 설정하라고 건의할 예정”이라고 말했다. 이에 대해 한 주류업체 관계자는 “국내 생산 매실주에서 검출되는 에틸카르바메이트 양은 외국의 허용치보다 낮다”고 설명했다.