오늘도 아침을 여는 코드카타..
수많은 불합리하고 말도안되는 알고리즘 문제 중 한가닥 공감을 사는 문제.. 흑흑
import java.util.ArrayList;
import java.util.Arrays;
class Solution {
public int[] solution(int[] answers) {
int[][] students = {
{1, 2, 3, 4, 5},
{2, 1, 2, 3, 2, 4, 2, 5},
{3, 3, 1, 1, 2, 2, 4, 4, 5, 5}
};
int[] scores = new int[students.length]; // score
for(int i=0; i< answers.length; i++) {
for (int st = 0; st < students.length; st++) {
if (answers[i] == students[st][i % students[st].length]) {
scores[st]++;
}
}
}
int maxScore = Arrays.stream(scores).max().orElse(0);
ArrayList<Integer> answer = new ArrayList<>();
for(int i =0; i < scores.length; i++) {
if(scores[i] == maxScore) {
answer.add(i + 1);
}
}
return answer.stream().mapToInt(Integer::intValue).toArray();
}
}
int[][] students : 수포자들(정상인들)의 정답 패턴을 작성int[] scores : 정상인들의 눈물겨운 결과를 저장할 배열for(int i=0; i< answers.length; i++): 정답 배열(answers)과 정답 패턴(students)를 비교하기 위해 answer의 정답을 순회for (int st = 0; st < students.length; st++)students[st][i % students[st].length]int maxScore = Arrays.stream(scores).max().orElse(0): 최고 점수 추출for(int i =0; i < scores.length; i++): 최고 점수인 수포자들 명단 추가class Solution {
public int solution(int[] nums) {
int cnt = 0;
for(int i = 0; i < nums.length - 2; i++) {
for(int j = i + 1; j < nums.length - 1; j ++) {
for(int k = j + 1; k < nums.length; k++) {
int sum = nums[i] + nums[j] + nums[k];
if (isPrime(sum)) {
cnt++;
}
}
}
}
return cnt;
public boolean isPrime(int num) {
if (num <= 1) return false;
for (int i = 2; i <= Math.sqrt(num); i++) {
if ( num % i == 0) return false;
}
return true;
}
}
조합을 어떻게 해야할 지 고민하는데 생각나는 가장 간단한 방법이 for문 돌리기였다
3중 for문이 되어버려서.. 저기만 시간복잡도가 O(n^3)가 될 것 같다
더 좋은 방법이 없을까. 이따 찾아봐야겠다.
데이터를 디스크 대신 메모리(RAM)에 저장하여 초고속 읽기 및 쓰기 성능을 제공하는 데이터베이스
디스크 기반 데이터베이스에 비해 지연 시간이 매우 낮아, 실시간 처리와 고성능을 요구하는 애플리케이션에서 사용됨
장점
단점
| 특징 | 인메모리 데이터베이스(IMDB) | 디스크 기반 데이터베이스 |
|---|---|---|
| 속도 | 매우 빠름 (RAM 기반) | 상대적으로 느림 (디스크 I/O) |
| 데이터 저장소 | 메모리 (RAM) | 디스크 |
| 데이터 영속성 | 메모리만 사용 시 영구성 부족 | 기본적으로 영구 저장 가능 |
| 확장성 | 메모리 용량에 제한 | 디스크 용량에 따라 대규모 데이터 저장 가능 |
| 비용 | 메모리 비용이 디스크보다 비쌈 | 디스크 기반으로 비용 효율적 |
| 사용 사례 | 캐싱, 세션 관리, 실시간 분석 | 트랜잭션, 복잡한 데이터 관계 저장 |
| 복잡한 쿼리 처리 | 제한적 | SQL 기반 복잡한 쿼리 지원 |
드디어rrrrrrrr Redis

(조건부) 오픈소스 인메모리 데이터 저장소
고속 데이터 처리를 위해 데이터를 메모리(RAM)에 저장하고 다양한 데이터 구조를 지원하는 NoSQL 데이터베이스
캐시, 메시지 브로커, 세션 저장소 등 다양한 용도로 사용 가능
| 데이터 구조 | 설명 | 사용 사례 |
|---|---|---|
| String | 단순 키-값 형태의 데이터 | 캐싱, 세션 관리 |
| Hash | 필드-값 쌍으로 구성된 해시 테이블 | 사용자 정보 저장 (ex: 프로필 정보) |
| List | 순서가 있는 데이터의 컬렉션 (양방향 삽입/삭제 지원) | 작업 큐, 채팅 메시지 |
| Set | 유일한 값들의 집합, 중복 허용하지 않음 | 태그 관리, 집합 연산 (교집합, 합집합) |
| Sorted Set | 정렬된 값들의 집합 (점수 기반) | 리더보드, 순위 관리 |
| Bitmap | 비트 수준의 값을 저장 및 조작 | 비트 플래그, 간단한 상태 관리 |
| HyperLogLog | 근사적(unique) 개수 계산 알고리즘 | 방문자 수 추정 |
| Streams | 데이터 스트림 처리 (로그, 이벤트) | 이벤트 스트림 분석, 실시간 데이터 처리 |
근사적(unique) 개수 계산 알고리즘: 대규모 데이터 집합에서 중복되지 않은 고유 요소(Unique Elements)의 개수를 빠르고 효율적으로 추정하기 위한 방법
| 명령어 | 설명 | 예제 |
|---|---|---|
SET key value | 키에 값을 저장 | SET user:1 "Alice" |
GET key | 키의 값을 가져옴 | GET user:1 |
HSET key field value | 해시에 필드와 값을 저장 | HSET user:1 name Alice |
HGET key field | 해시에서 필드의 값을 가져옴 | HGET user:1 name |
LPUSH key value | 리스트의 왼쪽에 값 추가 | LPUSH queue task1 |
LRANGE key start end | 리스트에서 특정 범위의 값을 가져옴 | LRANGE queue 0 -1 |
SADD key value | 집합에 값 추가 | SADD tags redis |
ZADD key score value | 정렬된 집합에 점수와 값을 추가 | ZADD leaderboard 100 user1 |
EXPIRE key seconds | 키에 TTL(만료 시간) 설정 | EXPIRE user:1 300 |
PUBLISH channel message | PUB/SUB에서 메시지 전송 | PUBLISH news "Hello" |
타입별 주요 명령어 정리
| 데이터 타입 | 명령어 | 설명 | 예제 |
|---|---|---|---|
| String | SET key value | 키에 값을 저장 | SET name Alice |
GET key | 키의 값을 가져옴 | GET name | |
DEL key | 키 삭제. | DEL name | |
INCR key | 키의 값을 1 증가 (숫자로 간주) | INCR counter | |
APPEND key value | 키에 값을 추가 | APPEND name " Smith" | |
| Hash | HSET key field value | 해시에 필드-값 쌍 저장 | HSET user:1 name Alice |
HGET key field | 특정 필드의 값을 가져옴 | HGET user:1 name | |
HDEL key field | 특정 필드 삭제 | HDEL user:1 age | |
HGETALL key | 모든 필드-값 쌍을 가져옴 | HGETALL user:1 | |
HEXISTS key field | 필드 존재 여부 확인 | HEXISTS user:1 name | |
| List | LPUSH key value | 리스트 왼쪽에 값 삽입. | LPUSH tasks "Task 1" |
RPUSH key value | 리스트 오른쪽에 값 삽입 | RPUSH tasks "Task 2" | |
LPOP key | 왼쪽 값 제거 및 반환 | LPOP tasks | |
RPOP key | 오른쪽 값 제거 및 반환 | RPOP tasks | |
LRANGE key start end | 리스트의 특정 범위 값을 반환 | LRANGE tasks 0 -1 | |
| Set | SADD key value | 집합에 값 추가 | SADD tags redis |
SREM key value | 집합에서 값 제거 | SREM tags redis | |
SMEMBERS key | 집합의 모든 값 반환 | SMEMBERS tags | |
SINTER key1 key2 | 두 집합의 교집합 반환 | SINTER tags1 tags2 | |
SUNION key1 key2 | 두 집합의 합집합 반환 | SUNION tags1 tags2 | |
| Sorted Set | ZADD key score value | 값과 점수를 추가 | ZADD leaderboard 100 user1 |
ZRANGE key start end | 특정 범위 값 반환 (오름차순) | ZRANGE leaderboard 0 -1 | |
ZREVRANGE key start end | 특정 범위 값 반환 (내림차순) | ZREVRANGE leaderboard 0 -1 | |
ZSCORE key value | 특정 값의 점수 반환 | ZSCORE leaderboard user1 | |
ZREM key value | 값 제거 | ZREM leaderboard user1 | |
| Bitmap | SETBIT key offset value | 특정 위치의 비트를 설정 | SETBIT user:1 0 1 |
GETBIT key offset | 특정 위치의 비트를 가져옴 | GETBIT user:1 0 | |
BITCOUNT key | 비트 1의 개수를 반환 | BITCOUNT user:1 | |
| HyperLogLog | PFADD key value | 값 추가 | PFADD visitors user1 |
PFCOUNT key | 고유 값 개수 추정 | PFCOUNT visitors | |
PFMERGE dest source | 여러 HyperLogLog 병합 | PFMERGE all_visitors day1 day2 | |
| Stream | XADD key id field value | 스트림에 데이터 추가 | XADD mystream * name Alice |
XRANGE key start end | 범위 내 스트림 데이터 조회 | XRANGE mystream - + | |
XREAD COUNT count STREAMS key id | 특정 ID 이후 데이터 읽기 | XREAD COUNT 2 STREAMS mystream 0 |
장점
단점
| 특징 | Redis | Memcached | RDBMS |
|---|---|---|---|
| 데이터 저장 방식 | Key-Value 및 다양한 데이터 구조 지원 | 단순 Key-Value 저장 | 관계형 데이터 모델 (테이블) |
| 속도 | 매우 빠름 | 매우 빠름 | 상대적으로 느림 |
| 데이터 구조 | String, Hash, List, Set 등 지원 | 단순 문자열 저장 | SQL 쿼리로 복잡한 데이터 처리 가능 |
| 데이터 영구 저장 | 지원 (RDB, AOF) | 지원하지 않음 | 기본적으로 지원 |
| 스케일링 | 클러스터링 및 샤딩 지원 | 샤딩 지원 | 수평 스케일링 제약, 샤딩 필요 |
| TTL 지원 | 기본적으로 지원 | 기본적으로 지원 | 별도 설정 필요 (트리거, 스케줄러) |
| 사용 사례 | 캐싱, 세션 관리, 실시간 애플리케이션 | 단순 캐싱 | 데이터 저장, 복잡한 쿼리 처리 |
| 메모리 사용량 | 효율적 (압축 가능) | 데이터 크기에 따라 증가 | 메모리 외 디스크 사용 |
| 복잡도 | 다양한 기능 제공으로 상대적으로 높음 | 단순한 설정과 사용 | 높은 복잡도 (스키마 설계 등) |
Memcached
오픈 소스 인메모리 데이터 캐싱 시스템
데이터베이스, API, 애플리케이션에서 데이터를 캐싱하여 요청 속도를 높이고, 성능과 확장성을 개선하는 데 사용
Redis, Memcached는 주로 캐싱 용도로 사용되지만, Redis는 비교적 폭넓은 데이터 구조와 영구 저장 기능으로 다용도로 활용됨
내 컴퓨터는 소중하니까 Docker compose를 이용해서 켜본다
services:
redis-stack:
image: redis/redis-stack
container_name: redis-stack-compose
restart: always
environment:
REDIS_ARGS: "--requirepass systempass"
ports:
- 6379:6379
- 8001:8001
"--requirepass systempass": 비밀번호를 systempass로 설정
정상적으로 띄워진 걸 확인하면, Intellij의 데이터베이스에서 확인할 수 있다.
database -> data source -> Redis
User: default
password: systempass


정상적으로 데이터가 삽입, 조회되는 것을 확인할 수 있다
localhost:8001 에 접속하면 레디스 인사이트 페이지가 출력된다

콘솔도 제공하고..

삽입한 key-value 값도 확인할 수 있다

이것저것 가능 (재밌다!)
Redis stack을 쓰면 내 컴퓨터에 있는 레디스에만 연결할 수 있다.
외부 레디스를 쓰는데 인사이트를 쓰고싶다면 앱스토어에서 다운받을 수 있다
또는 dmg 파일로 설치 진행 가능

이것저것 건드려 보다가 인텔리제이에 연결된 데이터베이스 데이터와 브라우저에서 확인한 데이터가 다름을 확인했다.
둘 다 db0에 연결되어 있는데 왜일까..?
이래저래 테스트해 본 결과, 레디스 컨테이너는 정상적으로 실행되고 있었고, 브라우저에서 확인할 수 있는 데이터베이스가 내가 연결하려 했던 레디스였음을 확인했다.
-> 인텔리제이에 연결된 레디스는 다른 것을 참조하고있다..!
컨테이너를 내리고 레디스가 여전히 6379에서 실행 중인지 확인
sudo lsof -i :6379

sudo kill -9 <PID>로 삭제 후 확인했으나 새로운 번호로 실행중인 레디스(!!)
자동 재시작 설정이 되어 있는 듯해 종료시켜줬다.
brew로 설치한 경우 brew services stop redis로 종료할 수 있다.

드디어 조용해졌다.
다시 컨테이너를 띄우고 확인해봤다.

정상 연동!