You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
6.1 KiB
173 lines
6.1 KiB
import os
|
|
import redis
|
|
import multiprocessing as mp
|
|
from concurrent.futures import ProcessPoolExecutor, as_completed
|
|
import time
|
|
import random
|
|
|
|
# ─────────────────────────────────────────────
|
|
# کانفیگ Redis
|
|
# ─────────────────────────────────────────────
|
|
REDIS_CONFIG = {
|
|
"host": "localhost",
|
|
"port": 6379,
|
|
"db": 0,
|
|
"decode_responses": True,
|
|
"socket_connect_timeout": 5,
|
|
"socket_keepalive": True,
|
|
}
|
|
|
|
NUM_WORKERS = 8
|
|
BATCH_SIZE = 100_000
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# بازه اعداد - مطابق با دادههای لود شده
|
|
# ═══════════════════════════════════════════════════════════════
|
|
TOTAL_RECORDS = 125_000_000
|
|
PHONE_START = 100_000_000
|
|
PHONE_END = PHONE_START + TOTAL_RECORDS - 1 # 224,999,999
|
|
|
|
|
|
def optimize_redis(r):
|
|
"""تنظیمات بهینه Redis"""
|
|
r.config_set("maxmemory", "16gb")
|
|
r.config_set("maxmemory-policy", "allkeys-lru")
|
|
r.config_set("save", "")
|
|
print("✓ Redis optimized")
|
|
|
|
|
|
def load_chunk_to_redis(args):
|
|
"""لود یک بخش از بازه اعداد به Redis"""
|
|
worker_id, start_phone, end_phone = args
|
|
r = redis.Redis(**REDIS_CONFIG)
|
|
try:
|
|
print(f"[Worker {worker_id}] Starting... phone: {start_phone:,} to {end_phone:,}")
|
|
|
|
loaded = 0
|
|
batch_count = 0
|
|
start_time = time.time()
|
|
|
|
pipe = r.pipeline()
|
|
current_phone = start_phone
|
|
|
|
while current_phone <= end_phone:
|
|
random_value = random.randint(1000, 9999)
|
|
pipe.set(str(current_phone), random_value)
|
|
loaded += 1
|
|
current_phone += 1
|
|
|
|
if loaded >= BATCH_SIZE * (batch_count + 1):
|
|
pipe.execute()
|
|
batch_count += 1
|
|
elapsed = time.time() - start_time
|
|
rate = loaded / elapsed if elapsed > 0 else 0
|
|
print(f"[Worker {worker_id}] Batch {batch_count} done ({loaded:,} rows, {rate:,.0f} rows/sec)")
|
|
|
|
if len(pipe.command_stack) > 0:
|
|
pipe.execute()
|
|
|
|
elapsed = time.time() - start_time
|
|
rate = loaded / elapsed if elapsed > 0 else 0
|
|
print(f"[Worker {worker_id}] Done! {loaded:,} rows in {elapsed:.1f}s ({rate:,.0f} rows/sec)")
|
|
return loaded
|
|
|
|
except Exception as e:
|
|
print(f"[Worker {worker_id}] ERROR: {e}")
|
|
raise
|
|
finally:
|
|
r.close()
|
|
|
|
|
|
def main():
|
|
print("=" * 60)
|
|
print("Redis Bulk Loader - Sequential Phone Numbers")
|
|
print("=" * 60)
|
|
print(f"Phone Range: {PHONE_START:,} to {PHONE_END:,}")
|
|
print(f"Total Records: {TOTAL_RECORDS:,}\n")
|
|
|
|
num_workers = min(NUM_WORKERS, mp.cpu_count())
|
|
print(f"Using {num_workers} workers\n")
|
|
|
|
r = redis.Redis(**REDIS_CONFIG)
|
|
try:
|
|
optimize_redis(r)
|
|
print("Clearing existing keys...")
|
|
r.flushdb()
|
|
finally:
|
|
r.close()
|
|
|
|
start_time = time.time()
|
|
|
|
try:
|
|
# ═══════════════════════════════════════════════════════
|
|
# تقسیم بازه اعداد بین workers
|
|
# ═══════════════════════════════════════════════════════
|
|
records_per_worker = TOTAL_RECORDS // num_workers
|
|
args = []
|
|
|
|
for i in range(num_workers):
|
|
if i < num_workers - 1:
|
|
start_phone = PHONE_START + (i * records_per_worker)
|
|
end_phone = PHONE_START + ((i + 1) * records_per_worker) - 1
|
|
else:
|
|
# آخرین worker باقیمانده را هم میگیرد
|
|
start_phone = PHONE_START + (i * records_per_worker)
|
|
end_phone = PHONE_END
|
|
|
|
args.append((i, start_phone, end_phone))
|
|
print(f"Worker {i}: {start_phone:,} to {end_phone:,} ({end_phone - start_phone + 1:,} records)")
|
|
|
|
print("\nStarting workers...\n")
|
|
|
|
with ProcessPoolExecutor(max_workers=num_workers) as executor:
|
|
futures = [executor.submit(load_chunk_to_redis, arg) for arg in args]
|
|
total = 0
|
|
|
|
for future in as_completed(futures):
|
|
try:
|
|
total += future.result()
|
|
except Exception as e:
|
|
print(f"Worker failed: {e}")
|
|
|
|
elapsed = time.time() - start_time
|
|
rate = total / elapsed if elapsed > 0 else 0
|
|
remaining = (TOTAL_RECORDS - total) / rate if rate > 0 else 0
|
|
percent = (total * 100) // TOTAL_RECORDS
|
|
|
|
print(f"\n>>> Progress: {total:,}/{TOTAL_RECORDS:,} ({percent}%) | "
|
|
f"Speed: {rate:,.0f} rows/sec | "
|
|
f"ETA: {remaining / 60:.1f} min")
|
|
|
|
print(f"\n✓ Total rows loaded: {total:,}")
|
|
|
|
# بررسی نهایی
|
|
r = redis.Redis(**REDIS_CONFIG)
|
|
try:
|
|
print("\n--- Sample Results ---")
|
|
sample_phones = [
|
|
PHONE_START,
|
|
PHONE_START + 10_000_000,
|
|
PHONE_START + 50_000_000,
|
|
PHONE_END
|
|
]
|
|
for phone in sample_phones:
|
|
value = r.get(str(phone))
|
|
print(f" {phone:,} → {value}")
|
|
|
|
final_count = r.dbsize()
|
|
print(f"\n✓ Final count in Redis: {final_count:,}")
|
|
finally:
|
|
r.close()
|
|
|
|
total_time = time.time() - start_time
|
|
print(f"\n✓ Done! Total time: {total_time / 60:.1f} minutes")
|
|
print(f"✓ Average speed: {TOTAL_RECORDS / total_time:,.0f} rows/sec")
|
|
|
|
except Exception as e:
|
|
print(f"\n✗ Error: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|