>헤드라인 >Redis 인터뷰 질문 요약, 알아보겠습니다

Redis 인터뷰 질문 요약, 알아보겠습니다

藏色散人
藏色散人앞으로
2019-02-22 13:21:4017357검색

다시 봄 채용 성수기와 이직 시즌이 다가오고 있습니다. 모든 프로그래머들이 코드를 입력할 생각을 하지 못하고 급여 인상을 향해 움직이기 시작하고 있습니까? 오늘은 Redis 면접 질문을 정리해보겠습니다!

주제 추천 : 2020 redis 면접 질문 모음(최신)

Redis 인터뷰 질문 요약, 알아보겠습니다

1. Redis란?

Redis는 메모리 기반의 고성능 키-값 데이터베이스입니다. (추천 1: Redis 동영상 튜토리얼) (추천 2: mysql 튜토리얼)

2. Reids의 특징 

Redis는 본질적으로 Memcached와 마찬가지로 전체 데이터베이스인 Key-Value 유형의 인메모리 데이터베이스입니다. 작업을 위해 메모리에 로드하고, 비동기 작업을 통해 주기적으로 데이터베이스 데이터를 하드 디스크에 플러시하여 저장합니다. 순수한 메모리 작업이기 때문에 Redis는 성능이 뛰어나고 초당 100,000회 이상의 읽기 및 쓰기 작업을 처리할 수 있는 것으로 알려진 가장 빠른 Key-Value DB입니다.

Redis의 우수성은 성능뿐만 아니라 다양한 데이터 구조 저장을 지원한다는 점이 가장 큰 장점입니다. 또한, 1MB의 데이터만 저장할 수 있는 memcached와 달리 단일 값의 최대 한도는 1GB입니다. 따라서 Redis는 List를 사용하여 FIFO 이중 연결 목록 만들기, 경량 고성능 메시지 대기열 서비스 구현, Set을 사용하여 고성능 태그 시스템 만들기 등과 같은 유용한 기능을 수행하는 데 사용할 수 있습니다. . 또한 Redis는 저장된 Key-Value의 만료 시간을 설정할 수도 있으므로 memcached의 향상된 버전으로도 사용할 수 있습니다.

Redis의 가장 큰 단점은 데이터베이스 용량이 물리적 메모리에 의해 제한되어 대용량 데이터의 고성능 읽기 및 쓰기에 사용할 수 없다는 것입니다. 따라서 Redis에 적합한 시나리오는 주로 고성능 작업 및 계산으로 제한됩니다. 더 적은 양의 데이터.

3. Redis를 사용하면 어떤 이점이 있나요?  

(1) HashMap과 마찬가지로 데이터가 메모리에 저장되기 때문에 빠릅니다. HashMap의 장점은 검색 및 연산의 시간 복잡도가 O(1) 이라는 것입니다.

(2) 풍부한 데이터 유형을 지원합니다. 문자열, 목록, 집합, 정렬 집합, 해시 지원

(3) 트랜잭션 지원, 작업은 원자성입니다. 소위 원자성은 데이터에 대한 모든 변경 사항이 실행되거나 전혀 실행되지 않음을 의미합니다.

(4) 풍부한 기능 : 캐싱, 메시지에 사용할 수 있으며 키로 만료 시간을 설정하면 만료 후 자동으로 삭제됩니다

4. memcached에 비해 redis의 장점은 무엇인가요?  

(1) memcached의 모든 값은 단순한 문자열이며, 이를 대체하는 redis는 더 풍부한 데이터 유형을 지원합니다.

(2) redis는 memcached보다 훨씬 빠릅니다. (3) redis는 이를 유지할 수 있습니다. Data

5 .Memcache와 Redis의 차이점은 무엇입니까?

1) 저장 방법 Memecache는 모든 데이터를 메모리에 저장합니다. 정전 후에는 데이터가 메모리 크기를 초과할 수 없습니다. Redis의 일부는 하드 디스크에 저장되어 데이터 지속성을 보장합니다.

2) 데이터 지원 유형 Memcache는 비교적 간단한 데이터 유형을 지원합니다. Redis에는 복잡한 데이터 유형이 있습니다.

3) 사용되는 기본 모델이 다릅니다. 클라이언트와의 통신을 위한 기본 구현 방법과 애플리케이션 프로토콜이 다릅니다. Redis는 자체 VM 메커니즘을 직접 구축했습니다. 일반 시스템이 시스템 기능을 호출하면 이동 및 요청에 일정 시간이 낭비되기 때문입니다.

6.Redis의 일반적인 성능 문제 및 해결 방법:  

1).Master는 메모리 스냅샷을 작성하고 save 명령은 메인 스레드의 작업을 차단하는 rdbSave 기능을 예약합니다. 성능에 미치는 영향은 매우 큽니다. 서비스가 간헐적으로 중단되므로 마스터가 메모리 스냅샷을 작성하지 않는 것이 가장 좋습니다.

2) 마스터 AOF 지속성. AOF 파일을 다시 작성하지 않으면 이 지속성 방법은 성능에 가장 작은 영향을 미치지만 AOF 파일이 너무 크면 복구에 영향을 미칩니다. 마스터 재시작 속도. 메모리 스냅샷 및 AOF 로그 파일을 포함하여 마스터에서 지속성 작업을 수행하지 않는 것이 가장 좋습니다. 특히 데이터가 중요한 경우 슬레이브는 AOF 백업 데이터를 활성화해야 하며 전략은 다음과 같습니다. 초당 한 번씩 동기화합니다.

3).Master는 AOF 파일을 다시 작성하기 위해 BGREWRITEAOF를 호출합니다. AOF는 다시 작성하는 동안 많은 양의 CPU 및 메모리 리소스를 차지하므로 과도한 서비스 로드가 발생하고 단기 서비스가 중단됩니다.

4) Redis 마스터-슬레이브 복제의 성능 문제는 마스터-슬레이브 복제 속도와 연결 안정성을 위해 슬레이브와 마스터가 동일한 LAN에 있는 것이 가장 좋습니다

7. MySQL에는 2천만 개의 데이터가 저장되어 있고 Redis에는 2천만 개의 데이터만 저장되어 있습니다. 20w의 데이터로 Redis의 데이터가 모두 핫 데이터인지 확인하는 방법

관련 지식: Redis 메모리 데이터 세트의 크기가 일정 규모 이상에서는 데이터 제거 전략(재활용 전략)이 구현됩니다. Redis는 6가지 데이터 제거 전략을 제공합니다.

휘발성-lru: 만료 시간이 설정된 데이터 세트(server.db[i].expires)에서 가장 최근에 사용된 데이터를 선택하여 제거합니다.

휘발성-ttl: 데이터 세트에서 만료 만료 날짜 설정 시간 데이터 세트(server.db[i].expires)에서 만료될 데이터를 선택하고 제거합니다

휘발성-random: 만료 시간이 설정된 데이터 세트(server.db[i].expires)에서 제거할 데이터를 무작위로 선택합니다.

allkeys-lru: 데이터 세트(server.db[i)에서 가장 최근 데이터를 선택합니다. ].dict) 가장 적게 사용되는 데이터 제거

allkeys-random: 제거를 위해 데이터 세트(server.db[i].dict)에서 임의로 데이터를 선택

no-enviction(eviction): 데이터 제거 금지

8 . Redis를 사용하고 모든 언어로 악성 로그인 보호 코드를 구현하여 각 사용자 ID를 1시간 내에 최대 5번의 로그인으로 제한하십시오. 특정 로그인 기능이나 기능의 경우 빈 기능을 사용하면 되며, 자세히 작성할 필요가 없습니다.

9. Redis는 왜 모든 데이터를 메모리에 저장해야 하나요?

가장 빠른 읽기 및 쓰기 속도를 달성하기 위해 Redis는 모든 데이터를 메모리에 읽고 데이터를 디스크에 비동기적으로 씁니다. 그래서 Redis는 빠른 속도와 데이터 지속성의 특징을 가지고 있습니다. 데이터가 메모리에 저장되지 않으면 디스크 I/O 속도가 Redis 성능에 심각한 영향을 미칩니다. 오늘날, 메모리가 점점 저렴해지면 redis가 점점 더 인기를 끌 것입니다.

최대 메모리 사용이 설정되어 있으면 데이터 레코드 수가 메모리 제한에 도달한 후에는 새 값을 삽입할 수 없습니다.

10.Redis는 단일 프로세스 및 단일 스레드입니다.

redis는 큐 기술을 사용하여 동시 액세스를 직렬 액세스로 전환하여 기존 데이터베이스 직렬 제어의 오버헤드를 제거합니다

11.

Redis는 대기열 모드를 사용하여 동시 액세스를 직렬 액세스로 전환하는 단일 프로세스 단일 스레드 모드입니다. Redis 자체에는 잠금 개념이 없으며 Redis는 여러 클라이언트 연결을 위해 경쟁하지 않습니다. 그러나 Jedis 클라이언트가 동시에 Redis에 액세스하는 경우 연결 시간 초과, 데이터 변환 오류, 차단, 클라이언트 연결 종료 등의 문제가 발생할 수 있습니다. 모두 클라이언트 연결 혼란으로 인해 발생합니다. 이 문제에 대한 해결책은 두 가지가 있습니다.

1. 클라이언트 관점에서 각 클라이언트가 Redis와 정상적이고 질서 있게 통신하도록 하기 위해 연결이 풀링되고 클라이언트 읽기 및 쓰기 Redis에 대해 내부 잠금이 동기화됩니다. 운영.

2. 서버 관점에서 setnx를 사용하여 잠금을 구현합니다.

참고: 첫 번째 유형의 경우 애플리케이션이 리소스 동기화를 자체적으로 처리해야 합니다. 사용할 수 있는 방법은 비교적 간단합니다. 두 번째 유형에서는 Redis의 setnx 명령을 사용해야 합니다. 몇 가지 문제에 주의를 기울여야 합니다.

12. Redis 사물 CAS(check-and-set 작업은 낙관적 잠금 구현)에 대한 이해?

다른 많은 데이터베이스와 마찬가지로 NoSQL 데이터베이스인 Redis도 트랜잭션 메커니즘을 제공합니다. Redis에서는 MULTI/EXEC/DISCARD/WATCH 네 가지 명령이 트랜잭션 구현의 초석입니다. 관계형 데이터베이스 개발 경험이 있는 개발자에게는 이 개념이 낯설지 않다고 생각합니다. 그럼에도 불구하고 Redis에서 트랜잭션의 구현 특성을 간략하게 나열하겠습니다.

1) 트랜잭션 중 모든 명령은 순차적으로 실행됩니다. 트랜잭션이 실행되면 Redis는 다른 클라이언트 요청에 대한 서비스를 제공하지 않으므로 트랜잭션의 모든 명령이 원자적으로 실행됩니다.

2) 관계형 데이터베이스의 트랜잭션과 비교하여 Redis 트랜잭션에서 명령이 실행되지 않으면 후속 명령이 계속 실행됩니다.

3) 관계형 데이터베이스 개발 경험이 있는 사람이라면 "BEGIN TRANSACTION" 문으로 이해할 수 있는 MULTI 명령을 통해 트랜잭션을 시작할 수 있습니다. 이 명령문 이후에 실행되는 명령은 트랜잭션 내의 작업으로 간주됩니다. 마지막으로 EXEC/DISCARD 명령을 실행하여 트랜잭션 내의 모든 작업을 커밋/롤백할 수 있습니다. 이 두 Redis 명령은 관계형 데이터베이스의 COMMIT/ROLLBACK 문과 동일한 것으로 간주될 수 있습니다.

4) 트랜잭션이 시작되기 전에 클라이언트와 서버 사이에 통신 장애가 발생하고 네트워크 연결이 끊어지면 이후에 실행될 모든 명령문이 서버에서 실행되지 않습니다. 그러나 클라이언트가 EXEC 명령을 실행한 후 네트워크 중단 이벤트가 발생하면 트랜잭션의 모든 명령이 서버에 의해 실행됩니다.

5) Append-Only 모드를 사용하는 경우 Redis는 write 시스템 함수를 호출하여 이 호출에서 트랜잭션의 모든 쓰기 작업을 디스크에 씁니다. 그러나 쓰기 프로세스 중에 정전으로 인한 가동 중지 시간과 같은 시스템 충돌이 발생하는 경우 현재 데이터의 일부만 디스크에 기록될 수 있으며 데이터의 다른 부분은 손실됩니다.

Redis 서버는 다시 시작할 때 일련의 필요한 일관성 검사를 수행합니다. 유사한 문제가 발견되면 즉시 종료되고 해당 오류 메시지가 표시됩니다. 이때 Redis 툴킷에 제공되는 redis-check-aof 도구를 최대한 활용해야 합니다. 이 도구는 데이터 불일치 오류를 찾고 작성된 일부 데이터를 롤백하는 데 도움이 될 수 있습니다. 복구 후 Redis 서버를 다시 시작할 수 있습니다.

13.WATCH 명령 및 CAS 기반 낙관적 잠금:

 Redis 트랜잭션에서 WATCH 명령을 사용하여 CAS(check-and-set) 기능을 제공할 수 있습니다. 트랜잭션이 실행되기 전에 WATCH 명령을 통해 여러 키를 모니터링한다고 가정합니다. WATCH 이후 키 값이 변경되면 EXEC 명령으로 실행된 트랜잭션이 중단되고 호출자에게 알리기 위해 Null 다중 대량 응답이 반환됩니다.

.

실행에 실패했습니다. 예를 들어, 키 값의 원자적 증가를 완료하기 위해 Redis에서 incr 명령이 제공되지 않는다고 다시 가정합니다. 이 기능을 구현하려면 해당 코드를 직접 작성하면 됩니다. 의사 코드는 다음과 같습니다.

val = GET mykey
val = val + 1
SET mykey $val

위 코드는 단일 연결의 경우에만 실행 결과가 정확하다는 것을 보장할 수 있습니다. 왜냐하면 여러 클라이언트가 동시에 이 코드를 실행하는 경우 다중 스레드 프로그램이 실행되기 때문입니다. - Race Condition(레이스 컨디션)에서 자주 발생하는 오류 시나리오가 나타납니다. 예를 들어 클라이언트 A와 B는 동시에 mykey의 원래 값을 읽습니다. 값이 10이라고 가정합니다. 그 후 두 클라이언트 모두 값에 1을 추가하고 이를 Redis 서버에 다시 설정합니다. 결과는 우리가 생각한 12가 아니라 11입니다. 비슷한 문제를 해결하려면 WATCH 명령의 도움이 필요합니다. 다음 코드를 참조하세요.

WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC

이전 코드와의 차이점은 새 코드에서는 mykey 값을 얻기 전에 먼저 WATCH 명령을 통해 키를 모니터링한다는 점입니다. 그런 다음 트랜잭션에 포함된 set 명령을 사용하면 각 연결이 EXEC를 실행하기 전에 현재 연결에서 얻은 mykey의 값이 연결된 다른 클라이언트에 의해 수정되는 경우 현재 연결의 EXEC 명령이 실행되지 않도록 효과적으로 보장할 수 있습니다. . 이런 방식으로 호출자는 반환 값을 판단한 후 val이 성공적으로 재설정되었는지 여부를 알 수 있습니다.

14. Redis 지속성을 위한 여러 가지 방법

1. 스냅샷

기본적으로 Redis는 데이터 스냅샷을 디스크의 바이너리 파일에 저장하며 파일 이름은 dump.rdb입니다. 예를 들어, N초마다 데이터 세트에 M개 이상의 업데이트가 있는 경우 데이터가 디스크에 기록되거나 SAVE 또는 BGSAVE 명령을 수동으로 호출할 수 있습니다.

작동 방식

. Redis 포크.

. 하위 프로세스는 임시 RDB 파일에 데이터를 쓰기 시작합니다.

. 하위 프로세스가 RDB 파일 쓰기를 마치면 이전 파일을 새 파일로 바꿉니다.

. 이 방법을 사용하면 Redis가 쓰기 중 복사 기술을 사용할 수 있습니다.

2. AOF

스냅샷 모드는 그다지 강력하지 않습니다. 시스템이 중지되거나 Redis가 실수로 종료되면 Redis에 기록된 마지막 데이터가 손실됩니다. 이는 일부 애플리케이션에서는 큰 문제가 아닐 수 있지만 높은 신뢰성이 요구되는 애플리케이션의 경우

Redis는 적합한 선택이 아닙니다.

추가 전용 파일 모드는 또 다른 옵션입니다.

구성 파일에서 AOF 모드를 활성화할 수 있습니다

3. 가상 메모리 방식

키가 작고 값이 클수록 VM을 사용하는 효과가 더 좋습니다.

키가 작지 않은 경우 몇 가지 특별한 방법을 사용하여 큰 키를 큰 값으로 바꾸는 것을 고려할 수 있습니다. 예를 들어 키와 값을 새로운 값으로 결합하는 것을 고려할 수 있습니다. 스레드 매개변수는 스왑 파일에 액세스하기 위한 스레드 수를 설정할 수 있습니다. 시스템의 코어 수를 초과하지 않는 것이 가장 좋습니다. 0으로 설정하면 스왑 파일에 대한 모든 작업이 직렬로 수행될 수 있습니다. 오랜 지연이 발생하지만 데이터의 무결성이 보장됩니다.

직접 테스트해 보니 가상 메모리를 사용하는 성능도 좋습니다. 데이터 양이 많은 경우 분산 또는 기타 데이터베이스를 고려할 수 있습니다

15. redis의 캐시 무효화 전략 및 기본 키 무효화 메커니즘

캐시 시스템은 잘못된 데이터를 정기적으로 정리해야 하므로 기본 키 무효화 및


Redis에서는 수명이 있는 키를 휘발성이라고 합니다. 캐시를 생성할 때 해당 키의 수명을 설정하세요. 키가 만료되면(수명이 0) 삭제될 수 있습니다.

1. 생존 시간에 영향을 미치는 일부 작업

DEL 명령을 사용하여 전체 키를 삭제하여 생존 시간을 제거하거나 SET 및 GETSET 명령으로 원본 데이터를 덮어쓸 수 있습니다. 즉, 값을 수정할 수 있습니다. 동일한 키와 값으로 덮어쓰면 현재 데이터의 생존 시간이 달라집니다.

예를 들어 키에 대해 INCR 명령을 실행하거나, 목록에 대해 LPUSH 명령을 실행하거나, 해시 테이블에 대해 HSET 명령을 실행하는 경우 이러한 작업은 키 자체의 생존 시간을 수정하지 않습니다. 반면에 RENAME을 사용하여 키 이름을 바꾸는 경우 이름이 바뀐 키의 생존 시간은 이름 바꾸기 전과 동일합니다.

RENAME 명령의 또 다른 가능성은 수명이 있는 키의 이름을 수명이 있는 another_key로 바꾸는 것입니다. 이때 이전 another_key(및 해당 수명)가 삭제되고 이전 키의 이름이 another_key로 변경됩니다. , 새로운 another_key의 생존 시간은 원래 키와 동일합니다. 키를 삭제하지 않고 키의 수명을 제거하여 키를 다시 영구 키로 만들려면 PERSIST 명령을 사용하십시오.

2. 생존 시간을 업데이트하는 방법

이미 생존 시간이 있는 키에 대해 EXPIRE 명령을 실행할 수 있으며, 새로 지정된 생존 시간이 이전 생존 시간을 대체합니다. 만료 시간의 정확도는 1ms 이내로 제어되었으며, 기본 키 오류의 시간 복잡도는 O(1)입니다.

EXPIRE와 TTL 명령을 함께 사용하면 키의 현재 생존 시간을 볼 수 있습니다. 설정이 성공하면 1을 반환하고, 키가 존재하지 않거나 키에 대해 생존 시간을 설정할 수 없으면 0을 반환합니다.

최대 캐시 구성

redis에서는 사용자가 최대 메모리 크기를 설정할 수 있습니다

server.maxmemory

默认为0,没有指定最大缓存,如果有新的数据添加,超过最大内存,则会使redis崩溃,所以一定要设置。redis 内存数据集大小上升到一定大小的时候,就会实行数据淘汰策略。

redis 提供 6种数据淘汰策略:

. volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

. allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

. no-enviction(驱逐):禁止驱逐数据

注意这里的6种机制,volatile和allkeys规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据,后面的lru、ttl以及random是三种不同的淘汰策略,再加上一种no-enviction永不回收的策略。

使用策略规则:

1、如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用allkeys-lru

2、如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random

三种数据淘汰策略:

ttl和random比较容易理解,实现也会比较简单。主要是Lru最近最少使用淘汰策略,设计上会对key 按失效时间排序,然后取最先失效的key进行淘汰

16.redis 最适合的场景  

Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,跟传统意义上的持久化有比较大的差别,那么可能大家就会有疑问,似乎Redis更像一个加强版的Memcached,那么何时使用Memcached,何时使用Redis呢?

如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点:

1 、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。

2 、Redis支持数据的备份,即master-slave模式的数据备份。

3 、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。

(1)、会话缓存(Session Cache)

最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?

幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。

(2)、全页缓存(FPC)

除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。

再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。

此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。

(3)、队列

Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。

如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。

(4),排行榜/计数器

Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:

当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:

ZRANGE user_scores 0 10 WITHSCORES

Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。

(5)、发布/订阅

最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!(不,这是真的,你可以去核实)。

Redis提供的所有特性中,我感觉这个是喜欢的人最少的一个,虽然它为用户提供如果此多功能。

相关推荐:

Redis教程

2020年前端vue面试题大汇总(附答案)

성명:
이 기사는 PHP开源社区에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제