本文目录导读:

我来详细介绍Python操作数据库分区表的方法,主要针对PostgreSQL和MySQL两种常见数据库。
PostgreSQL分区表操作
创建分区表
import psycopg2
from psycopg2.extras import RealDictCursor
def create_partitioned_table():
"""创建分区表"""
conn = psycopg2.connect(
host="localhost",
port=5432,
database="testdb",
user="user",
password="password"
)
cur = conn.cursor()
# 创建主表(分区表)
cur.execute("""
CREATE TABLE IF NOT EXISTS sales (
id SERIAL,
sale_date DATE NOT NULL,
amount NUMERIC(10,2),
product_id INTEGER
) PARTITION BY RANGE (sale_date)
""")
# 创建分区
cur.execute("""
CREATE TABLE IF NOT EXISTS sales_2024_q1 PARTITION OF sales
FOR VALUES FROM ('2024-01-01') TO ('2024-04-01')
""")
cur.execute("""
CREATE TABLE IF NOT EXISTS sales_2024_q2 PARTITION OF sales
FOR VALUES FROM ('2024-04-01') TO ('2024-07-01')
""")
conn.commit()
cur.close()
conn.close()
# 自动创建分区
def auto_create_partitions(start_date, end_date, interval_months=3):
"""自动创建季度分区"""
conn = psycopg2.connect(**db_config)
cur = conn.cursor()
current = start_date
while current < end_date:
# 计算分区边界
next_date = current + datetime.timedelta(days=interval_months * 30)
partition_name = f"sales_{current.strftime('%Y_%m')}"
cur.execute(f"""
CREATE TABLE IF NOT EXISTS {partition_name} PARTITION OF sales
FOR VALUES FROM ('{current.date()}') TO ('{next_date.date()}')
""")
current = next_date
conn.commit()
cur.close()
conn.close()
查询分区表信息
def get_partition_info():
"""获取分区信息"""
conn = psycopg2.connect(**db_config)
cur = conn.cursor(cursor_factory=RealDictCursor)
# 查看分区表信息
cur.execute("""
SELECT
parent.relname AS parent_table,
child.relname AS partition_name,
pg_get_expr(child.relpartbound, child.oid) AS partition_boundary,
pg_size_pretty(pg_relation_size(child.oid)) AS size
FROM pg_inherits
JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
JOIN pg_class child ON pg_inherits.inhrelid = child.oid
WHERE parent.relname = 'sales'
ORDER BY child.relname
""")
partitions = cur.fetchall()
for partition in partitions:
print(f"分区名: {partition['partition_name']}")
print(f"边界: {partition['partition_boundary']}")
print(f"大小: {partition['size']}")
print("---")
cur.close()
conn.close()
MySQL分区表操作
使用mysql-connector-python
import mysql.connector
from mysql.connector import Error
def create_mysql_partitioned_table():
"""创建MySQL分区表"""
conn = mysql.connector.connect(
host="localhost",
user="root",
password="password",
database="testdb"
)
cursor = conn.cursor()
# 创建RANGE分区表
cursor.execute("""
CREATE TABLE IF NOT EXISTS orders (
id INT AUTO_INCREMENT,
order_date DATE NOT NULL,
amount DECIMAL(10,2),
customer_id INT,
PRIMARY KEY (id, order_date)
) PARTITION BY RANGE (YEAR(order_date)) (
PARTITION p2020 VALUES LESS THAN (2021),
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p_future VALUES LESS THAN MAXVALUE
)
""")
# 创建LIST分区表
cursor.execute("""
CREATE TABLE IF NOT EXISTS customers_by_region (
id INT AUTO_INCREMENT,
name VARCHAR(50),
region VARCHAR(20),
PRIMARY KEY (id, region)
) PARTITION BY LIST COLUMNS(region) (
PARTITION p_north VALUES IN ('北京', '天津', '河北'),
PARTITION p_south VALUES IN ('广东', '深圳', '海南'),
PARTITION p_east VALUES IN ('上海', '江苏', '浙江'),
PARTITION p_other VALUES IN ('其他')
)
""")
conn.commit()
cursor.close()
conn.close()
管理MySQL分区
class MySQLPartitionManager:
def __init__(self, connection_config):
self.config = connection_config
def add_partition(self, table_name, partition_name, value):
"""添加新分区"""
conn = mysql.connector.connect(**self.config)
cursor = conn.cursor()
try:
cursor.execute(f"""
ALTER TABLE {table_name}
REORGANIZE PARTITION p_future INTO (
PARTITION {partition_name} VALUES LESS THAN ({value}),
PARTITION p_future VALUES LESS THAN MAXVALUE
)
""")
conn.commit()
print(f"分区 {partition_name} 添加成功")
except Error as e:
print(f"添加分区失败: {e}")
conn.rollback()
finally:
cursor.close()
conn.close()
def drop_partition(self, table_name, partition_name):
"""删除分区"""
conn = mysql.connector.connect(**self.config)
cursor = conn.cursor()
try:
cursor.execute(f"""
ALTER TABLE {table_name} DROP PARTITION {partition_name}
""")
conn.commit()
print(f"分区 {partition_name} 删除成功")
except Error as e:
print(f"删除分区失败: {e}")
conn.rollback()
finally:
cursor.close()
conn.close()
def truncate_partition(self, table_name, partition_name):
"""清空分区数据"""
conn = mysql.connector.connect(**self.config)
cursor = conn.cursor()
cursor.execute(f"""
ALTER TABLE {table_name} TRUNCATE PARTITION {partition_name}
""")
conn.commit()
cursor.close()
conn.close()
使用SQLAlchemy操作分区表
ORM方式操作分区表
from sqlalchemy import create_engine, text, MetaData, Table, Column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Integer, String, Date, Numeric
Base = declarative_base()
class Sale(Base):
"""定义分区表模型(PostgreSQL)"""
__tablename__ = 'sales'
__table_args__ = {
'postgresql_partition_by': 'RANGE (sale_date)',
'schema': 'public'
}
id = Column(Integer, primary_key=True)
sale_date = Column(Date, nullable=False)
amount = Column(Numeric(10, 2))
product_id = Column(Integer)
class PartitionManager:
def __init__(self, database_url):
self.engine = create_engine(database_url)
self.Session = sessionmaker(bind=self.engine)
def create_partitions(self):
"""使用原生SQL创建分区"""
with self.engine.connect() as conn:
# 创建分区
conn.execute(text("""
CREATE TABLE IF NOT EXISTS sales_2024_01 PARTITION OF sales
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01')
"""))
conn.execute(text("""
CREATE TABLE IF NOT EXISTS sales_2024_02 PARTITION OF sales
FOR VALUES FROM ('2024-02-01') TO ('2024-03-01')
"""))
conn.commit()
def insert_data_with_partition(self, data):
"""插入数据到分区表"""
session = self.Session()
try:
for record in data:
sale = Sale(**record)
session.add(sale)
session.commit()
except Exception as e:
session.rollback()
raise e
finally:
session.close()
def query_specific_partition(self, partition_name):
"""查询特定分区"""
with self.engine.connect() as conn:
result = conn.execute(
text(f"SELECT * FROM {partition_name} WHERE amount > :min_amount"),
{"min_amount": 1000}
)
return result.fetchall()
自动化分区维护脚本
import schedule
import time
from datetime import datetime, timedelta
class AutoPartitionMaintenance:
def __init__(self, db_type='postgresql', **connection_params):
self.db_type = db_type
self.connection_params = connection_params
def create_next_month_partition(self):
"""创建下个月的分区"""
next_month = datetime.now() + timedelta(days=30)
partition_name = f"sales_{next_month.strftime('%Y_%m')}"
if self.db_type == 'postgresql':
self._create_pg_partition(partition_name, next_month)
elif self.db_type == 'mysql':
self._create_mysql_partition(partition_name, next_month)
def _create_pg_partition(self, name, date):
conn = psycopg2.connect(**self.connection_params)
cur = conn.cursor()
start_date = date.replace(day=1)
if start_date.month == 12:
end_date = start_date.replace(year=start_date.year+1, month=1)
else:
end_date = start_date.replace(month=start_date.month+1)
cur.execute(f"""
CREATE TABLE IF NOT EXISTS {name} PARTITION OF sales
FOR VALUES FROM ('{start_date.date()}') TO ('{end_date.date()}')
""")
conn.commit()
cur.close()
conn.close()
def cleanup_old_partitions(self, keep_months=6):
"""清理旧分区"""
cutoff_date = datetime.now() - timedelta(days=keep_months*30)
conn = psycopg2.connect(**self.connection_params)
cur = conn.cursor()
# 获取所有分区
cur.execute("""
SELECT child.relname, pg_get_expr(child.relpartbound, child.oid)
FROM pg_inherits
JOIN pg_class child ON pg_inherits.inhrelid = child.oid
JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
WHERE parent.relname = 'sales'
""")
for partition_name, boundary in cur.fetchall():
# 解析分区边界,判断是否超过保留期限
if self._should_drop_partition(partition_name, boundary, cutoff_date):
cur.execute(f"DROP TABLE IF EXISTS {partition_name}")
print(f"已删除旧分区: {partition_name}")
conn.commit()
cur.close()
conn.close()
def _should_drop_partition(self, name, boundary, cutoff_date):
"""判断是否需要删除分区"""
# 从分区名中提取日期信息
try:
# 假设分区名格式为 sales_2024_01
date_str = name.split('_')[1:]
partition_date = datetime(int(date_str[0]), int(date_str[1]), 1)
return partition_date < cutoff_date
except:
return False
# 定时任务
def main():
manager = AutoPartitionMaintenance(
db_type='postgresql',
host='localhost',
database='testdb',
user='user',
password='password'
)
# 每天早上8点创建下个月分区
schedule.every().day.at("08:00").do(manager.create_next_month_partition)
# 每周一清理旧分区
schedule.every().monday.at("03:00").do(
manager.cleanup_old_partitions, keep_months=6
)
while True:
schedule.run_pending()
time.sleep(60)
if __name__ == "__main__":
main()
性能监控和分析
def analyze_partition_performance():
"""分析分区表性能"""
conn = psycopg2.connect(**db_config)
cur = conn.cursor()
# 检查分区裁剪效果
cur.execute("""
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM sales
WHERE sale_date >= '2024-01-01'
AND sale_date < '2024-02-01'
""")
print("查询计划:")
for row in cur.fetchall():
print(row[0])
# 获取分区统计信息
cur.execute("""
SELECT
schemaname,
tablename,
n_live_tup as row_count,
n_dead_tup as dead_rows,
last_analyze,
last_autoanalyze
FROM pg_stat_user_tables
WHERE tablename LIKE 'sales_%'
ORDER BY tablename
""")
print("\n分区表统计:")
for row in cur.fetchall():
print(f"表名: {row[1]}, 行数: {row[2]}, 死元组: {row[3]}")
cur.close()
conn.close()
注意事项
- 分区键选择:选择合适的分区键,通常是时间或地理字段
- 分区数量控制:避免创建过多分区,影响数据库性能
- 索引策略:分区表索引需要考虑全局索引和本地索引
- 数据迁移:大型分区表可能需要特定工具进行数据迁移
- 备份策略:分区表的备份可能需要特殊处理
示例覆盖了Python操作数据库分区表的主要场景,可以根据实际需求选择合适的方案。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播