当前位置: 首页 > news >正文

PostgreSQL——分区表

分区表

  • 一、分区表的意义
  • 二、传统分区表
    • 2.1、继承表
    • 2.2、创建分区表
    • 2.3、使用分区表
    • 2.4、查询父表还是子表
    • 2.5、constraint_exclusion参数
    • 2.6、添加分区
    • 2.7、删除分区
    • 2.8、分区表相关查询
    • 2.9、传统分区表注意事项
  • 三、内置分区表
    • 3.1、创建分区表
    • 3.2、使用分区表
    • 3.3、内置分区表原理
    • 3.4、添加分区
    • 3.5、删除分区
    • 3.6、更新分区数据
    • 3.7、内置分区表注意事项

一、分区表的意义

分区表主要有以下优势 :

  • 当查询或更新一个分区上的大部分数据时,对分区进行索引扫描代价很大,然而,在分区上使用顺序扫描能提升性能 。
  • 当需要删除一个分区数据时,通过 DROP TABLE 删除一个分区,远比 DELETE 删除数据高效,特别适用于日志数据场景。
  • 由于一个表只能存储在一个表空间上,使用分区表后,可以将分区放到不同的表空间上,例如可以将系统很少访问的分区放到廉价的存储设备上,也可以将系统常访问的分区存储在高速存储上。

二、传统分区表

传统分区表是通过继承和触发器方式实现的,其实现过程步骤多,非常复杂,需要定义父表、定义子表、定义子表约束、创建子表索引、创建分区插入、删除、修改函数和触发器等,可以说是在普通基础上手动实现的分区表。在介绍传统分区表之前先介绍继承,继承是传统分区表的重要组成部分。

2.1、继承表

PostgreSQL提供继承表,简单地说是首先定义一张父表,之后可以创建子表继承父表,下面通过一个简单的例子来理解。

创建一张日志模型表tbl_log:

create table tbl_log(id int4,create_date date,log_type text
);create table tbl_log_sql(sql text
) inherits(tbl_log);

通过inherits(tbl_log)表示表tbl_log_sql继承表tbl_log,子表可以定义额外的字段,以上定义了sql为额外字段,其它字段则继承父表tbl_log,查看tbl_log_sql表结构如下:
在这里插入图片描述

父表和子表都可以插入数据,接着分别在父表和子表中插入一条数据,如下所示:

insert into tbl_log
values(1, '2017-08-26', null);insert into tbl_log_sql
values(2, '2017-08-27', null, 'select 2');

这时如果查询父表tbl_log会显示两表的记录,如下所示:

select * from tbl_log;

在这里插入图片描述

尽管查询父表会将子表的记录数也列出,但子表自定义的字段没有显示,如果想确定数据来源于哪张表,可通过以下SQL查看表的OID,如下所示:

select tableoid, *
from tbl_log;

在这里插入图片描述

tableoid是表的隐藏字段,表示表的OID,可以通过pg_class系统关联找到表名,如下所示:

select p.relname, c.*
from tbl_log c, pg_class p
where c.tableoid = p.oid;

如果只想查询父表的数据,需在父表名称前加上关键字ONLY,如下所示:

select * 
from only tbl_log;

在这里插入图片描述

因此,对于UPDATE、DELETE、SELECT操作,如果父表名称前面没有加上ONLY,则会对父表和所有子表进行DML操作,如下所示:

delete from tbl_log;select count(*) 
from tbl_log;

在这里插入图片描述

2.2、创建分区表

传统分区表创建过程主要包括以下几个步骤:

  1. 创建父表,如果父表上定义了约束,子表会继承,因此除非是全局约束,否则不应该在父表上定义约束,另外,父表不应该写入数据。
  2. 通过inherits方式创建继承表,也称之为子表或分区,子表的字段定义应该和父表保持一致。
  3. 给所有子表创建约束,只有满足约束条件的数据才能写入对应分区,注意分区约束值范围不要有重叠。
  4. 给所有子表创建索引,由于继承操作不会继承父表上的索引,因此索引需要手工创建。
  5. 在父表上定义insert、delete、update触发器,将SQL分发到对应分区,这步可选,因为应用可以根据分区规则定位到对应分区进行DML操作。
  6. 启用constraint_exclusion参数,如果这个参数设置成off,则父表上的SQL性能会降低,后面会通过示例解释这个参数。

以上六个步骤是创建传统分区表的主要步骤,接下来通过一个示例演示创建一张范围分区表,并且定义年月子表存储月数据。

首先创建父表:

create table log_ins(id serial,user_id int4,create_time timestamp(0) without time zone
);

创建13张子表:

create table log_ins_history(check(create_time < '2017-01-01')
) inherits(log_ins);create table log_ins_history_201701(check(create_time >= '2017-01-01' and create_time < '2017-02-01')
) inherits(log_ins);create table log_ins_history_201702(check(create_time >= '2017-02-01' and create_time < '2017-03-01')
) inherits(log_ins);create table log_ins_history_201703(check(create_time >= '2017-03-01' and create_time < '2017-04-01')
) inherits(log_ins);create table log_ins_history_201704(check(create_time >= '2017-04-01' and create_time < '2017-05-01')
) inherits(log_ins);create table log_ins_history_201705(check(create_time >= '2017-05-01' and create_time < '2017-06-01')
) inherits(log_ins);create table log_ins_history_201706(check(create_time >= '2017-06-01' and create_time < '2017-07-01')
) inherits(log_ins);create table log_ins_history_201707(check(create_time >= '2017-07-01' and create_time < '2017-08-01')
) inherits(log_ins);create table log_ins_history_201708(check(create_time >= '2017-08-01' and create_time < '2017-09-01')
) inherits(log_ins);create table log_ins_history_201709(check(create_time >= '2017-09-01' and create_time < '2017-10-01')
) inherits(log_ins);create table log_ins_history_201710(check(create_time >= '2017-10-01' and create_time < '2017-11-01')
) inherits(log_ins);create table log_ins_history_201711(check(create_time >= '2017-11-01' and create_time < '2017-12-01')
) inherits(log_ins);create table log_ins_history_201712(check(create_time >= '2017-12-01' and create_time < '2018-01-01')
) inherits(log_ins);

给子表创建索引:

create index idx_his_ctime on log_ins_history 
using btree (create_time);create index idx_log_ins_201701_ctime on log_ins_history_201701 
using btree (create_time);create index idx_log_ins_201702_ctime on log_ins_history_201702
using btree (create_time);create index idx_log_ins_201703_ctime on log_ins_history_201703
using btree (create_time);create index idx_log_ins_201704_ctime on log_ins_history_201704
using btree (create_time);create index idx_log_ins_201705_ctime on log_ins_history_201705
using btree (create_time);create index idx_log_ins_201706_ctime on log_ins_history_201706
using btree (create_time);create index idx_log_ins_201707_ctime on log_ins_history_201707
using btree (create_time);create index idx_log_ins_201708_ctime on log_ins_history_201708
using btree (create_time);create index idx_log_ins_201709_ctime on log_ins_history_201709
using btree (create_time);create index idx_log_ins_201710_ctime on log_ins_history_201710
using btree (create_time);create index idx_log_ins_201711_ctime on log_ins_history_201711
using btree (create_time);create index idx_log_ins_201712_ctime on log_ins_history_201712
using btree (create_time);

由于父表上不存储数据,可以不用在父表上创建索引。

创建触发器函数,设置数据插入父表时的路由规则,如下所示:

create or  replace function log_ins_insert_trigger()returns triggerlanguage plpgsql
as $function$
beginif (NEW.create_time < '2017-01-01') theninsert into log_ins_history VALUES(NEW.*);elsif (NEW.create_time >= '2017-01-01' and NEW.create_time < '2017-02-01') theninsert into log_ins_history_201701 VALUES(NEW.*);elsif (NEW.create_time >= '2017-02-01' and NEW.create_time < '2017-03-01') theninsert into log_ins_history_201702 VALUES(NEW.*);elsif (NEW.create_time >= '2017-03-01' and NEW.create_time < '2017-04-01') theninsert into log_ins_history_201703 VALUES(NEW.*);elsif (NEW.create_time >= '2017-04-01' and NEW.create_time < '2017-05-01') theninsert into log_ins_history_201704 VALUES(NEW.*);elsif (NEW.create_time >= '2017-05-01' and NEW.create_time < '2017-06-01') theninsert into log_ins_history_201705 VALUES(NEW.*);elsif (NEW.create_time >= '2017-06-01' and NEW.create_time < '2017-07-01') theninsert into log_ins_history_201706 VALUES(NEW.*);elsif (NEW.create_time >= '2017-07-01' and NEW.create_time < '2017-08-01') theninsert into log_ins_history_201707 VALUES(NEW.*);elsif (NEW.create_time >= '2017-08-01' and NEW.create_time < '2017-09-01') theninsert into log_ins_history_201708 VALUES(NEW.*);elsif (NEW.create_time >= '2017-09-01' and NEW.create_time < '2017-10-01') theninsert into log_ins_history_201709 VALUES(NEW.*);elsif (NEW.create_time >= '2017-10-01' and NEW.create_time < '2017-11-01') theninsert into log_ins_history_201710 VALUES(NEW.*);elsif (NEW.create_time >= '2017-11-01' and NEW.create_time < '2017-12-01') theninsert into log_ins_history_201711 VALUES(NEW.*);elsif (NEW.create_time >= '2017-12-01' and NEW.create_time < '2018-01-01') theninsert into log_ins_history_201712 VALUES(NEW.*);else raise exception 'create_time out of range. Fix the log_ins_insert trigger() function!';end if;return null;
end;
$function$;

函数中的NEW.*是指要插入的数据行,在父表上定义插入触发器:

create trigger insert_log_ins_trigger
before insert
on log_ins
for each row
execute procedure log_ins_insert_trigger();

触发器创建完成后,向父表log_ins插入数据时,会执行触发器并触发函数log_ins_insert_trigger()将表数据插入到相应的分区中。DELETE、UPDATE触发器和函数创建过程和INSERT方式类似,传统分区表的创建步骤已全部完成。

2.3、使用分区表

向父表log_ins插入测试数据,并验证数据是否插入对应分区:

insert into log_ins(user_id, create_time)
select round(10000000*random()), generate_series('2016-12-01'::date, '2017-12-01'::date, '1 minute');

这里通过随机生成数据插入,数据如下所示:

select * 
from log_ins limit 5;

在这里插入图片描述

查看父表数据,发现父表没有数据;

select count(*) from only log_ins;

在这里插入图片描述

select count(*) from log_ins;

在这里插入图片描述

查看子表数据:

select min(create_time), max(create_time)
from log_ins_history_201701;

在这里插入图片描述

这说明子表里可查到数据,查看子表大小:
在这里插入图片描述

由此可见数据都已经插入到子表里。

2.4、查询父表还是子表

假如我们检索2017-01-01这一天的数据,我们可以查询父表,也可以直接查询子表,两者性能上是否有差异?

查询父表的执行计划如下:

explain analyze select *
from log_ins 
where create_time > '2017-01-01' and create_time < '2017-01-02';

在这里插入图片描述

从以上执行计划看出在分区log_ins_history_201701上进行了索引扫描,接着查看直接查询子表log_ins_history_201701的执行计划:

explain analyze select *
from log_ins_history_201701
where create_time > '2017-01-01' and create_time < '2017-01-02';

在这里插入图片描述

从以上执行计划看出,直接查询子表更快,如果并发量上去的话,这个差异将更明显,因此实际生产过程中,对于传统分区表分区方式,不建议应用访问父表,而是直接访问子表,也许有人会问,应用如何定位到访问哪张子表呢?可以根据预先的分区约束定义,上面的例子是根据时间范围分区,name应用可以根据时间来判断查询哪张子表,当然,以上是根据分表表分区键查询的场景,如果根据非分区键查询则会扫描分区表的所有分区。

2.5、constraint_exclusion参数

constraint_exclusion参数用来控制优化器是否根据表上的约束来优化查询,参数为以下值:

  • on:所有表都通过约束优化查询
  • off:所有表都不通过约束优化查询
  • partition:只对继承表和UNION ALL子查询通过检索约束来优化查询

简单地说,如果设置成on或partition,查询父表时优化器会根据子表上的约束判断检索哪些子表,而不要扫描所有子表,从而提升查询性能。

2.6、添加分区

添加分区属于分区表维护的常规操作之一,比如历史表范围分区到期之前需要扩分区,log ins表为日志表,每个分区存储当月数据,假如分区快到期了,可通过以下SQL扩分区,首先创建子表,如下所示:

create table log_ins_history_201801(check(create_time >= '2018-01-01' and create_time < '2018-02-01')
) inherits(log_ins);

之后创建相关索引:

create index idx_log_ins_201801_ctime 
on log_ins_history_201801 using btree(create_time);

然后刷新触发器函数log_ins_insert_trigger(0,添加相应代码,将符合路由规则的数据插入新分区,详见之前定义的这个函数,这步完成后,添加分区操作完成,可通过d+log_ins命令查看log_ins的所有分区。

这种方法比较直接,创建分区时就将分区继承到父表,如果中间步骤有错可能对生产系统带来影响,比较推荐的做法是将以上操作分解成以下几个步骤,降低对生产系统的影响,如下所示:

-- 创建分区
create table log_ins_history_201802(like log_ins including all
);-- 添加约束
alter table log_ins_history_201802 
add constraint log_ins_history_201802_create_time_check
check (create_time >= '2018-02-01' and create_time < '2018-03-01');-- 刷新触发器函数log_ins_insert_trigger()-- 所有步骤完成后,将新分区log_ins_201802继承到父表log_ins
alter table log_ins_hisroty_201802 inherit log_ins;

以上方法是将新分区所有操作完成后,再将分区继承到父表,降低了生产系统添加分区操作的风险,当然,在生产系统添加分区前建议在测试环境事先演练一把。

2.7、删除分区

分区表的一个重要优势是对于大表的管理上十分方便,例如需要删除历史数据时可以直接删除一个分区,这比DELETE方式效率高了多个数量级,传统分区表删除分区通常有两种方法,第一种方法是直接删除分区,如下所示:

drop table log_ins_201802;

就像删除普通表一样删除分区即可,当然删除分区前需再三确认是否需要备份数据;另一种比较推荐的删除分区方法是先将分区的继承关系去掉,如下所示:

alter table log_ins_history_201802 no inherit log_ins;

执行以上命令后,log ins201802分区不再属于分区表log_ins的分区,但log_ins_history_201802表依然保留可供查询,这种方式相比方法一提供了一个缓冲时间,属于比较稳妥的删除分区方法,因为在拿掉子表继承关系后,只要没删除这个表,还可以使子表重新继承父表。

2.8、分区表相关查询

分区表创建完成后,如何查看分区表定义、分区表分区信息呢?比较常用的方法是通过\d元命令,如下所示:
在这里插入图片描述

以上信息显示了表 log_ ins有 14 个分区,并且创 建了触发器 ,触发器函数为 log_ins_insert_ trigger (),如 果想列出分区名称可通过\d+ log_ins 元命令列出 。

另一种列出分区表分区信息方法是通过SQL命令:

select nmsp_parent.nspname as parent_schema,parent.relname as parent,nmsp_child.nspname as child_schema,child.relname as child_schema
from pg_inherits join pg_class parent
on pg_inherits.inhparent = parent.oid join pg_class child
on pg_inherits.inhrelid = child.oid join pg_namespace nmsp_parent
on nmsp_parent.oid = parent.relnamespace join pg_namespace nmsp_child
on nmsp_child.oid = child.relnamespace
where parent.relname = 'log_ins';

在这里插入图片描述

pg_inherits系统表记录了子表和父表之间的继承关系,通过以上查询列出指定分区表的分区。如果想查看一个库中有哪些分区表,并显示这些分区表的分区数量,可通过以下SQL查询:

select nspname, relname, count(*) as partition_num
from pg_class c, pg_namespace n, pg_inherits i
where c.oid = i.inhparent 
and c.relnamespace = n.oid
and c.relhassubclass
and c.relkind in ('r', 'p')
group by 1,2 
order by partition_num desc;

在这里插入图片描述
以上结果显示当前库中有两个分区表,log_ins分区表有14个分区,tbl_log分区表只有一个分区。

2.9、传统分区表注意事项

传统分区表的使用有以下注意事项:

  • 当往父表上插入数据时,需事先在父表上创建路由函数和触发器,数据才会根据分区键路由规则插入到对应分区中,目前仅支持范围分区和列表分区。
  • 分区表上的索引、约束需要使用单独的命令创建,目前没有办法一次性自动在所有分区上创建索引、约束。
  • 父表和子表允许单独定义主键,因此父表和子表可能存在重复的主键记录,目前不支持在分区表上定义全局主键。
  • UPDATE时不建议更新分区键数据,特别是会使数据从一个分区移动到另一分区的场景,可通过更新触发器实现,但会带来管理上的成本。
  • 性能方面:根据本节的测试数据和测试场景,传统分区表根据非分区键查询相比普通表性能差距较大,因为这种场景下分区表会扫描所有分区;根据分区键查询相比普通表性能有小幅降低,而查询分区表子表性能相比普通表略有提升;

三、内置分区表

PostgreSQL10一个重量级新特性是支持内置分区表,用户不需要预先在父表上定义INSERT、DELETE、UPDATE触发器,对父表的DML操作会自动路由到相应分区,相比传统分区表大幅度降低了维护成本,目前仅支持范围分区和列表分区。

3.1、创建分区表

创建分区表的主要语法包含两部分:创建主表和创建分区。
创建主表语法如下:

create table table_name (...)
[ partition by {range | list}  ({column_name | expression})] 

创建主表时须指定分区方式,可选的分区方式为RANGE范围分区或LIST列表分区,并指定字段或表达式作为分区键。

创建分区的语法如下:

create table table_name() partition of parent_table for values partition_bound_spec

创建分区时必须指定是哪张表的分区,同时指定分区策略partition bound spec,如果是范围分区,partition bound spec须指定每个分区分区键的取值范围,如果是列表分区
partition_bound_spec,需指定每个分区的分区键值。

PostgreSQL10创建内置分区表主要分为以下几个步骤:

  1. 创建父表,指定分区键和分区策略。
  2. 创建分区,创建分区时须指定分区表的父表和分区键的取值范围,注意分区键的范围不要有重叠,否则会报错。
  3. 在分区上创建相应索引,通常情况下分区键上的索引是必须的,非分区键的索引可根据实际应用场景选择是否创建。

接下来通过创建范围分区的示例来演示内置分区表的创建过程,首先创建一张范围分区表,表名为log_par,如下所示:

create table log_par (id serial,user_id int4,create_time timestamp(0) without time zone
) partition by range(create_time);

表log par指定了分区策略为范围分区,分区键为create time字段。创建分区,并设置分区的分区键取值范围,如下所示:

create table log_par_his partition of log_par 
for values from ('2016-01-01') to ('2017-01-01');create table log_par_201701 partition of log_par 
for values from ('2017-01-01') to ('2017-02-01');create table log_par_201702 partition of log_par 
for values from ('2017-02-01') to ('2017-03-01');create table log_par_201703 partition of log_par 
for values from ('2017-03-01') to ('2017-04-01');create table log_par_201704 partition of log_par 
for values from ('2017-04-01') to ('2017-05-01');create table log_par_201705 partition of log_par 
for values from ('2017-05-01') to ('2017-06-01');create table log_par_201706 partition of log_par 
for values from ('2017-06-01') to ('2017-07-01');create table log_par_201707 partition of log_par 
for values from ('2017-07-01') to ('2017-08-01');create table log_par_201708 partition of log_par 
for values from ('2017-08-01') to ('2017-09-01');create table log_par_201709 partition of log_par 
for values from ('2017-09-01') to ('2017-10-01');create table log_par_201710 partition of log_par 
for values from ('2017-10-01') to ('2017-11-01');create table log_par_201711 partition of log_par 
for values from ('2017-11-01') to ('2017-12-01');create table log_par_201712 partition of log_par 
for values from ('2017-12-01') to ('2018-01-01');

注意分区的分区键范围不要有重叠,定义分区键范围实质上给分区创建了约束。

给所有分区的分区键创建索引,如下所示:

create index idx_log_par_his_ctime
on log_par_his using btree(create_time);create index idx_log_par_201701_ctime
on log_par_201701 using btree(create_time);create index idx_log_par_201702_ctime
on log_par_201702 using btree(create_time);create index idx_log_par_201703_ctime
on log_par_201703 using btree(create_time);create index idx_log_par_201704_ctime
on log_par_201704 using btree(create_time);create index idx_log_par_201705_ctime
on log_par_201705 using btree(create_time);create index idx_log_par_201706_ctime
on log_par_201706 using btree(create_time);create index idx_log_par_201707_ctime
on log_par_201707 using btree(create_time);create index idx_log_par_201708_ctime
on log_par_201708 using btree(create_time);create index idx_log_par_201709_ctime
on log_par_201709 using btree(create_time);create index idx_log_par_201710_ctime
on log_par_201710 using btree(create_time);create index idx_log_par_201711_ctime
on log_par_201711 using btree(create_time);create index idx_log_par_201712_ctime
on log_par_201712 using btree(create_time);

以上三步完成了内置分区表的创建。

3.2、使用分区表

向分区表插入数据:

insert into log_par(user_id, create_time)
select round(100000 * random()), generate_series('2016-12-01'::date, '2017-12-01'::"date", '1 minute');

查看表数据:

select count(*)
from log_par;

在这里插入图片描述

select count(*)
from only log_par;

在这里插入图片描述

从以上结果可以看出,父表log par没有存储任何数据,数据存储在分区中,通过分区大小也可以证明这一点,如下所示:
在这里插入图片描述

3.3、内置分区表原理

内置分区表原理实际上和传统分区表一样,也是使用继承方式,分区可称为子表,通过以下查询很明显看出表log par和其分区是继承关系:

select nmsp_parent.nspname as parent_schema,parent.relname as parent,nmsp_child.nspname as child_schema,child.relname as child_schema
from pg_inherits join pg_class parent
on pg_inherits.inhparent = parent.oid join pg_class child
on pg_inherits.inhrelid = child.oid join pg_namespace nmsp_parent
on nmsp_parent.oid = parent.relnamespace join pg_namespace nmsp_child
on nmsp_child.oid = child.relnamespace
where parent.relname = 'log_par';

在这里插入图片描述

3.4、添加分区

添加分区的操作 比较简单,例如给 log_par 增加一个分区,如下所示 :

create table log_par_201801 partition of log_par
for values from ('2018-01-01') to ('2018-02-01');

之后给分区创建索引,如下所示 :

create index idx_log_par_201801_ctime 
on log_par_201801 using btree(create_time);

3.5、删除分区

删除分区有两种方法,第一种方法通过 DROP 分区的方式来删除,如下所示 :

drop table log_par_201801;

D ROP 方式直接将分 区 和分 区数据删除,删除前需确 认分区数据是否需要备份,避免数据丢失;另 一种推荐的方法是解绑分区, 如下所示 :

alter table log_par detach partition log_par_201801;

绑分区只是将分区 和 父表间 的关系断开 ,分区和分区数据依然保留 ,这种方式比较稳妥,如果后续需要恢复这个分区,通过连接分区方式恢复分区即可,如下所示 :

alter table log_par attach partition log_par_201801 for values from ('2018-01-01') to ('2018-02-01');

连接分区时需要指定分区上的约束 。

3.6、更新分区数据

内置分区 表 UPDAT E 操作目前不支持新记录跨分区的情况, 也就是说只允许分区 内的更新 , 例如以下 SQL 会报错:

update log_par set create_time = '2017-02-02 01:01:01' 
where user_id = 16965492;

以上 user_id 等于 16965492 的记录位于 log_par_201701 分区,将这条记录的 create_time 更新为 ’ 2017-02-02 01 : 01:01 ’由于违反了当前分区的约束将报错,如果更新的数据不违反当前分区的约束则可正常更新数据,如下所示:

update log_par set create_time = '2017-01-01 01:01:01' 
where user_id = 16965492;

目前内置分区表的这一 限制对于日志表影响不大,对于业务表有一定影响,使用时需注意 。

3.7、内置分区表注意事项

  • 当往父表上插入数据时,数据会自动根据分区键路由规则插入到分区中,目前仅支持范围分区和列表分区。
  • 分区表上的索引、约束需使用单独的命令创建,目前没有办法一次性自动在所有分区上创建索引、约束。
  • 内置分区表不支持定义(全局)主键,在分区表的分区上创建主键是可以的。
  • 内置分区表的内部实现使用了继承。
  • 如果UPDATE语句的新记录违反当前分区键的约束则会报错,UPDAET语句的新记录目前不支持跨分区的情况。
  • 性能方面:根据本节的测试场景,内置分区表根据非分区键查询相比普通表性能差距较大,因为这种场景分区表的执行计划会扫描所有分区;根据分区键查询相比普通表性能有小幅降低,而查询分区表子表性能相比普通表略有提升。

文章转载自:

http://o7yyhMi2.fwkjp.cn
http://1DxjOizN.fwkjp.cn
http://o8e0CtW5.fwkjp.cn
http://HD9ZvODG.fwkjp.cn
http://XKFY3UQI.fwkjp.cn
http://GyAxl6ka.fwkjp.cn
http://jpeWezTw.fwkjp.cn
http://3WHWYOoa.fwkjp.cn
http://H5E6dUaQ.fwkjp.cn
http://3jhECXNU.fwkjp.cn
http://KMwvWiqj.fwkjp.cn
http://48lDQmdt.fwkjp.cn
http://ECiA48IV.fwkjp.cn
http://z15YMGri.fwkjp.cn
http://7JGLWN96.fwkjp.cn
http://mzjrpef7.fwkjp.cn
http://8xOW65tm.fwkjp.cn
http://zYAxqRpc.fwkjp.cn
http://ihTqD0sr.fwkjp.cn
http://6zBge46q.fwkjp.cn
http://7OXYFYX9.fwkjp.cn
http://H6ZHGezn.fwkjp.cn
http://SHtqLmzh.fwkjp.cn
http://NnpH8imc.fwkjp.cn
http://p8aUP02f.fwkjp.cn
http://XZEeOguu.fwkjp.cn
http://LEgOx53X.fwkjp.cn
http://TYFbBXhJ.fwkjp.cn
http://JS2kHLkc.fwkjp.cn
http://cfIbQQVy.fwkjp.cn
http://www.dtcms.com/a/384390.html

相关文章:

  • Elastic APM 高级特性:分布式追踪与机器学习优化
  • Ubuntu 服务器配置转发网络访问
  • Redis 数据结构源码剖析(SDS、Dict、Skiplist、Quicklist、Ziplist)
  • C#通讯之网络通讯 TCP UDP
  • 响应时间从5ms到0.8ms:威迈斯AI+DSP协同架构的突破与工程实践
  • 《WINDOWS 环境下32位汇编语言程序设计》第16章 WinSock接口和网络编程(2)
  • 算法--插入排序
  • 领码方案|权限即数据:企业系统中的字段级访问控制架构实战(Ver=1.0)
  • 【面试场景题】支付金融系统与普通业务系统的一些技术和架构上的区别
  • 数证杯顺心借JAVA网站重构详细版(服务器取证基础考点+检材+题目+重构视频)
  • 【Unity】【Photon】Fusion2中的玩家输入系统 学习笔记
  • Vue3 + Three.js 实战:自定义 3D 模型加载与交互全流程
  • 【Leetcode hot 100】102.二叉树的层序遍历
  • [Windows] 微软 .Net 运行库离线安装包 | Microsoft .Net Packages AIO_v09.09.25
  • java通过RESTful API实现两个项目之间相互传输数据
  • C++基础(13)——list类的模拟实现
  • C#/.NET/.NET Core技术前沿周刊 | 第 54 期(2025年9.8-9.14)
  • 快速上手 Jenkins
  • 【C++】STL--List使用及其模拟实现
  • Go语言双向链表list.List详解
  • 机器学习-Boosting
  • Jenkins运维之路(Jenkins流水线改造Day02-2-容器项目)
  • 【C++STL】list的详细用法和底层实现
  • Elastic APM 与 Elasticsearch 集成:构建完整可观测性栈
  • 从零搭建MCP Server:Python开发、部署与应用全流程实战
  • Mac本地Docker拉取镜像本地挂载项目
  • 购物车效果
  • 在Ubuntu 18.0.4 编译最新版Python-3.13.7
  • 如何在ubuntu下用pip安装aider,解决各种报错问题
  • Redis 高可用实战源码解析(Sentinel + Cluster 整合应用)