wei/测试/c.py

174 lines
6.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from pymongo import MongoClient, ReadPreference
import time
from datetime import datetime
# ============ 配置区 ============
# 使用副本集连接字符串,这是实现读写分离和高可用的关键[citation:2]
REPLICA_SET_URI = "mongodb://192.168.124.10:37017,192.168.124.11:37017/?replicaSet=rs0"
DATABASE_NAME = "rw_split_test"
COLLECTION_NAME = "orders"
# ================================
def setup_connection():
"""建立具备读写分离能力的连接"""
print("=== 1. 建立智能连接 ===")
# read_preference=SECONDARY_PREFERRED 是关键[citation:2]
# 写操作会自动路由到主节点PRIMARY
# 读操作会优先路由到从节点SECONDARY若从节点不可用则回退到主节点
client = MongoClient(
REPLICA_SET_URI,
read_preference=ReadPreference.SECONDARY_PREFERRED,
serverSelectionTimeoutMS=5000
)
try:
# 测试连接并获取副本集信息
client.admin.command('ping')
print("✅ 已成功连接到副本集")
# 显示当前主节点(写操作将发往此处)
is_master = client.admin.command('isMaster')
print(f" 当前主节点: {is_master.get('primary', '未知')}")
print(f" 读取偏好模式: SECONDARY_PREFERRED (优先从库读)")
db = client[DATABASE_NAME]
collection = db[COLLECTION_NAME]
# 可选:清空旧测试数据
# collection.delete_many({})
return client, db, collection
except Exception as e:
print(f"❌ 连接失败: {e}")
return None, None, None
def test_write_operations(collection):
"""测试写操作:验证是否全部发往主节点"""
print("\n=== 2. 测试写操作 (强制发往主节点) ===")
test_docs = [
{"item": "apple", "price": 5, "quantity": 10, "timestamp": datetime.now()},
{"item": "banana", "price": 2, "quantity": 20, "timestamp": datetime.now()},
]
try:
# 插入多条数据
result = collection.insert_many(test_docs)
print(f"✅ 插入 {len(result.inserted_ids)} 条订单数据")
print(f" 文档ID: {result.inserted_ids}")
# 更新一条数据
update_result = collection.update_one(
{"item": "apple"},
{"$set": {"price": 6, "updated_at": datetime.now()}}
)
print(f"✅ 更新了 {update_result.modified_count} 条数据 (苹果涨价)")
return result.inserted_ids
except Exception as e:
print(f"❌ 写操作失败: {e}")
return []
def test_read_operations(collection, doc_ids):
"""测试读操作:展示如何在主从节点间分配"""
print("\n=== 3. 测试读操作 (在主从节点间分配) ===")
if not doc_ids:
print("⚠ 无有效数据,跳过读测试")
return
try:
# 测试1普通查询根据连接设置的 SECONDARY_PREFERRED优先从从节点读[citation:2]
print("\n[测试1] 普通查询 (策略: SECONDARY_PREFERRED)")
all_orders = list(collection.find())
print(f" 查询到 {len(all_orders)} 条订单")
for order in all_orders[:2]: # 显示前两条
print(f" - {order['item']}: 单价{order.get('price')}元, 数量{order.get('quantity')}")
# 测试2强制从主节点读用于需要读取最新写入数据的场景[citation:2]
print("\n[测试2] 强制从主节点读 (策略: PRIMARY)")
# 通过 with_options 临时覆盖读取偏好[citation:2]
primary_read = collection.with_options(read_preference=ReadPreference.PRIMARY)
latest_order = primary_read.find_one(sort=[("timestamp", -1)])
if latest_order:
print(f" 从主节点读取的最新订单: {latest_order.get('item')}, 时间: {latest_order.get('timestamp')}")
# 测试3模拟多次读请求观察负载分配
print("\n[测试3] 模拟多次查询 (观察读请求分配)")
print(" 进行5次快速查询...")
for i in range(5):
count = collection.count_documents({})
# 注意:由于驱动内部优化,连续快速请求可能命中同一节点
time.sleep(0.1) # 短暂间隔
print(f" 当前总订单数: {count}")
except Exception as e:
print(f"❌ 读操作失败: {e}")
def monitor_replication_delay(client, db_name, coll_name):
"""监控主从复制延迟"""
print("\n=== 4. 检查复制状态 ===")
try:
admin_db = client.admin
# 获取副本集状态
status = admin_db.command('replSetGetStatus')
print(" 成员状态:")
for member in status['members']:
state_str = member['stateStr']
health = '健康' if member['health'] == 1 else '异常'
lag = member.get('optimeDate', 'N/A')
print(f" - {member['name']}: {state_str} ({health})")
# 检查从节点是否可读
# 在MongoDB Shell中需要使用 rs.slaveOk()在PyMongo中设置read_preference即可[citation:7]
print(f" 说明: 驱动已通过 read_preference 设置处理从节点读取,无需手动执行 rs.slaveOk()")
except Exception as e:
print(f" 复制状态检查受限: {e}")
def main():
print("=" * 60)
print("MongoDB 副本集读写分离测试")
print("=" * 60)
client, db, collection = setup_connection()
if not client:
return
try:
# 测试写入
inserted_ids = test_write_operations(collection)
# 等待2秒确保数据有足够时间复制到从节点
print("\n⏳ 等待2秒让数据同步到从节点...")
time.sleep(2)
# 测试读取
test_read_operations(collection, inserted_ids)
# 检查复制状态
monitor_replication_delay(client, DATABASE_NAME, COLLECTION_NAME)
# 最终验证:从从节点查询写入的数据
print("\n=== 5. 最终验证 ===")
# 创建一个明确只从从节点读的集合句柄SECONDARY模式[citation:2]
secondary_collection = collection.with_options(read_preference=ReadPreference.SECONDARY)
try:
# 此查询应该成功,因为数据已复制
verify_data = secondary_collection.find_one({"item": "banana"})
if verify_data:
print(f"✅ 验证成功!已从从节点读取到数据: {verify_data['item']}")
else:
print("⚠ 警告:暂未从从节点读取到最新数据,可能复制延迟稍高")
except Exception as e:
print(f"❌ 从节点读取验证失败: {e} (可能从节点尚未同步完成或不可用)")
finally:
client.close()
print("\n" + "=" * 60)
print("测试完成,连接已关闭")
print("=" * 60)
if __name__ == "__main__":
main()