Oracle物化视图与物化视图日志
文章目录
- 物化视图
- 物化视图与普通视图的区别
- 创建一个存放person的表
- 创建一个存放person的address的表
- 初始化数据
- 创建物化视图的语句
- 1.build [immediate|deferred]
- 2.refresh [fast|complete|force] 视图刷新的方式:
- 3.MV数据刷新的时间:
- 4.查询重写(QueryRewrite)
- 演示 refresh complete on demand
- 演示 build deferred
- 演示 refresh complete on commit
- 演示on commit情况下的物化视图更新出错
- 简单总结什么情况下不适合使用物化视图
- 如何查看物化视图
- 如何使用job去刷新物化视图
- 删除schedule job
- 演示如何创建一个schedule job来刷新物化视图
- 如何删除物化视图
- 物化视图日志
- 物化视图的Fast刷新
- [67000][439] ORA-00439: feature not enabled: Advanced replication
- 创建物化视图日志
- 演示创建物化视图with primary key
- including new values
- 演示创建物化视图with rowid
- 实现物化视图的增量刷新
- 使用物化视图增量刷新的优缺点
- oracle通过v$sql视图查询sql的平均执行时间
以下都是最近我通过网上学习和实践简单总结的一些学习成果,还有很多还是不了解。
物化视图
物化视图与普通视图的区别
物化视图是一种特殊的物理表,“物化”(Materialized)视图是相对普通视图而言的。普通视图是虚拟表,应用的局限性大,任何对视图的查询, Oracle 都实际上转换为视图SQL语句的查询。这样对整体查询性能的提高,并没有实质上的好处。
而物化视图是一张实际存在的表,是占有数据库磁盘空间的。
物化视图并不像普通视图那样,只有在使用的时候才去读取数据,而是预先计算并保存表连接或者聚集等比较耗时操作的结果,这样大大提高了读取的速度,特别适合抽取大数据量表的某些信息。
创建一个存放person的表
create table test_person(pid int,name varchar2(20));
alter table test_person add constraint pk_pid primary key(pid);
--删除表
drop table test_person;
创建一个存放person的address的表
create table test_address(aid int,address varchar2(20),pid int);
alter table test_address add constraint pk_aid primary key(aid);
--删除表
drop table test_address;
初始化数据
insert into test_person values(1,'kevin');
insert into test_address values(1,'SHA',1);
insert into test_person values(2,'vincent');
insert into test_address values(2,'HKG',2);
COMMIT;
创建物化视图的语句
create materialized view [view_name]
build [immediate|deferred]
refresh [fast|complete|force]
[
on [commit|demand] |
start with (start_time) next (next_time)
] [enable | disable] query rewrite
as
{创建物化视图用的查询语句}
1.build [immediate|deferred]
创建方式(BuildMethods):包括BUILD IMMEDIATE
和BUILD DEFERRED
两种。
BUILD IMMEDIATE
是在创建物化视图的时候就生成数据。BUILD DEFERRED
则在创建时不生成数据,以后根据需要在生成数据。默认为BUILD IMMEDIATE
2.refresh [fast|complete|force] 视图刷新的方式:
fast
: 增量刷新.假设前一次刷新的时间为t1,那么使用fast模式刷新物化视图时,只向视图中添加t1到当前时间段内,主表变化过的数据.为了记录这种变化,建立增量刷新物化视图还需要一个物化视图日志表。create materialized view log on (主表名)
。complete
:全部刷新。相当于重新执行一次创建视图的查询语句。force
: 这是默认的数据刷新方式。当可以使用fast模式时,数据刷新将采用fast方式;否则使用complete方式。
3.MV数据刷新的时间:
on demand
:在用户需要刷新的时候刷新,这里就要求用户自己动手去刷新数据了(也可以使用job定时刷新)on commit
:当主表中有数据提交的时候,立即刷新MV中的数据;start ……
:从指定的时间开始,每隔一段时间(由next指定)就刷新一次;
4.查询重写(QueryRewrite)
包括ENABLE QUERY REWRITE
和DISABLE QUERY REWRITE
两种。
分别指出创建的物化视图是否支持查询重写。查询重写是指当对物化视图的基表进行查询时,Oracle会自动判断能否通过查询物化视图来得到结果,如果可以,则避免了聚集或连接操作,而直接从已经计算好的物化视图中读取数据。默认为DISABLEQUERY REWRITE
。
感觉很少用,有兴趣的可以研究下:https://mp.weixin.qq.com/s/5Lg8cSEb3R5WkQwVF9xfPQ
演示 refresh complete on demand
create materialized view my_mv_cd
build immediate refresh complete on demand
as
select p.pid as id, p.name, a.address from test_person p,test_address a
where p.pid = a.aid;
这里的build immediate
表示创建物化视图的同时立即导入数据。
BUILD DEFERRED
则在创建时不生成数据,以后根据需要在生成数据。默认为BUILD IMMEDIATE
。
在demand
模式下,修改基表,物化视图是不会跟着改变的,只能手动DBMS_MVIEW.REFRESH
等方法来进行刷新,也可以通过JOB定时进行刷新
演示 build deferred
create materialized view my_mv_cd2
build deferred refresh complete on demand
as
select p.pid as id, p.name, a.address from test_person p,test_address a
where p.pid = a.aid;
如何手动刷新?
BEGIN
DBMS_MVIEW.REFRESH (
list => 'my_mv_cd2',
Method =>'C',
refresh_after_errors => True);
END;
演示 refresh complete on commit
create materialized view my_mv_cc
build immediate refresh complete on commit
as
select p.pid as id, p.name, a.address from test_person p,test_address a
where p.pid = a.aid;
insert into test_person values(3,'thomas');
insert into test_address values(3,'ZHA',3);
COMMIT;
一旦基表有了COMMIT,即事务提交,则立刻刷新,立刻更新物化视图,使得数据和基表一致。缺点就是对基表的DML操作影响很大。
演示on commit情况下的物化视图更新出错
而且对基表的DML跟对物化视图的刷新应该是在同一个事务下的。所以如果物化视图更新出错,主表也无法提交事务。
我们可以做如下的一个实验。。
创建两个表,不设置主键。
create table test_person2(pid int,name varchar2(20));
create table test_address2(aid int,address varchar2(20),pid int);
创建物化视图,刷新时间模式设置为on commit
create materialized view my_mv_test
refresh complete on commit
as
select p.pid as id, p.name, a.address from test_person2 p,test_address2 a
where p.pid = a.aid;
然后我们给id加一个唯一索引。
create unique index u_test_index on my_mv_test(id);
现在开始插入数据。。我们插入相同id的数据
insert into test_person2 values(1,'kevin');
insert into test_address2 values(1,'SHA',1);
insert into test_person2 values(1,'vincent');
insert into test_address2 values(1,'HKG',1);
COMMIT;
然后我们可以发现两个基表也没有数据提交,说明是在同一个事务下进行了rollback。
所以使用commit需要注意的地方:
- 主表提交时基表也提交,系统开销增大。在繁忙的OLTP系统中会影响一部分性能。如果物化视图更新出错,主表也无法提交事务。
- 主表和物化视图必须在同一个数据库中
- 无法在基表上执行分布式事务
- 不支持含有对象类型或Oracle补充支持类型的物化视图
更多的参考:http://blog.itpub.net/29047826/viewspace-1575568/
简单总结什么情况下不适合使用物化视图
1.不适合建立物化视图的情况: 基表有频繁的DML操作 / (on commit刷新时很费时,大大增加DML操作的时间)
2.多表连接的物化视图仅适合 refresh complete 定时刷新,且刷新时间不能小于30min,否则弊大于利(complete是删除原有数据重新连接多表数据)
如何查看物化视图
SELECT * FROM user_mviews;
SELECT * FROM all_mviews;
如何使用job去刷新物化视图
begin
dbms_scheduler.create_job(job_name => 'sam_job', --job名job_type => 'STORED_PROCEDURE', --job类型job_action => 'pc_sam', --存储过程名start_date => sysdate, --开始执行时间repeat_interval => 'FREQ=MINUTELY;INTERVAL=5', --下次执行时间,每5分钟执行存储过程pc_samcomments => '测试存储过程', --注释auto_drop => false, --job禁用后是否自动删除enabled => true);
end;
/
1、job_name
: 任务名称
2、job_type
:有三种类型,PL/SQL Block
、Stored procedure
、Executable
3、job_action
:根据job_type的不同,有不同的含义
- 如果
job_type
指定的是存储过程,就需要指定存储过程的名字; - 如果
job_type
指定的是PL/SQL块,就需要输入完整的PL/SQL代码; - 如果
job_type
指定的外部程序,就需要输入script的名称或者操作系统的指令名
4、start_date
:开始时间
5、repeat_interval
:运行的时间间隔,上面例子是每天23点运行一次
6、end_date
:到期时间
7、enabled
:创建后自动激活
8、auto_drop
:默认true,即当job执行完毕都到期是否直接删除job
9、comments
:备注
关于schedule时间设置可以查看官网:https://docs.oracle.com/cd/E11882_01/appdev.112/e40758/d_sched.htm#ARPLS72235
关于可以repeat_interval设置可以参考:https://blog.csdn.net/zq9017197/article/details/6985109
删除schedule job
BEGINDBMS_SCHEDULER.DROP_JOB(job_name => 'my_mv_cd_job');
END;
/
演示如何创建一个schedule job来刷新物化视图
首先我们创建一个存储过程,用来刷新我们的物化视图
create or replace procedure my_mv_cd_procedure_job is
begindbms_mview.refresh('my_mv_cd');
end my_mv_cd_procedure_job;
/
然后我们可以通过如下的sql来查询我们的存储过程。。
SELECT * FROM all_source where type = 'PROCEDURE';SELECT * FROM user_source where type = 'PROCEDURE';
然后我们创建一个schedule job来trigger我们的刚刚定义的存储过程。。
BEGIN
DBMS_SCHEDULER.CREATE_JOB (job_name => 'my_mv_cd_job',job_type => 'STORED_PROCEDURE', --指定执行的是存储过程job_action => 'my_mv_cd_procedure_job', --对应的存储过程名称repeat_interval => 'FREQ=MINUTELY; INTERVAL=1', --1分钟trigger一次enabled => true);
END;
/
然后我们可以通过如下sql查询我们创建的schedule job
--查询
select * from ALL_SCHEDULER_JOBS;
select * from USER_SCHEDULER_JOBS;
我们可以看到执行时间是相隔了1分钟
我们可以试着插入数据
insert into test_person values(4,'justin');
insert into test_address values(4,'HKG',4);
commit;
1分钟之后就会发现物化视图trigger刷新了。
如何删除物化视图
drop materialized view {物化视图名称};
物化视图日志
物化视图的Fast刷新
前面说到物化视图是可以进行Fast刷新,也就是增量刷新,当每次刷新的时候不必对整个物化视图进行刷新,只需要对有改动的record进行刷新。
那db是如何知道有哪些record有change呢?
通过对基表建立物化视图日志,就可以监测到基表的变动,每当基表有变动的时候就会往对应的物化视图日志中写入数据。当进行Fast刷新的时候,db就会把物化视图日志中的改动写入到物化视图中,这样就实现了增量的刷新。
[67000][439] ORA-00439: feature not enabled: Advanced replication
create materialized view log on test_kevin with primary key including new values;
当我在本地尝试创建物化视图日志的时候发生了如下错误:
[67000][439] ORA-00439: feature not enabled: Advanced replication
我们可以通过如下sql查看我们db开启的功能:
select * from v$option where parameter = 'Advanced replication';
通过查看我们可以知道我们的高级复制功能是没有被开启的。不知道是当时安装有问题还是可能这是一个加钱才会有的功能吧。
创建物化视图日志
如果我们想要创建fast模式刷新的物化视图就一定要对关联的基表都需要创建物化视图日志。
Oracle 的物化视图的快速刷新要求必须建立物化视图日志,通过物化视图日志可以实现增量刷新功能。
- 物化视图日志在建立时有多种选项:可以指定为
ROWID
、PRIMARY KEY
和OBJECTID
几种类型,同时还可以指定SEQUENCE
或明确指定列名。不过上面这些情况产生的物化视图日志的结构都不相同。 - 当发生DML 操作时,内部的触发器会把变化记录到物化视图日志里,也就是说物化视图不支持DDL的同步,所以在物化视图的编写过程中不可使用
select * from
的形式,因为这样当基表发生变化时,物化视图就会失效。
物化视图日志的名称为MLOG$_后面跟基表的名称,如果表名的长度超过20位,则只取前20位,当截短后出现名称重复时,Oracle会自动在物化视图日志名称后面加上数字作为序号。
任何物化视图都会包括的4列:
SNAPTIME$$
:用于表示刷新时间。DMLTYPE$$
:用于表示DML操作类型,I表示INSERT,D表示DELETE,U表示UPDATE。OLD_NEW$$
:用于表示这个值是新值还是旧值。N(EW)表示新值,O(LD)表示旧值,U表示UPDATE操作。CHANGE_VECTOR$$
:表示修改矢量,用来表示被修改的是哪个或哪几个字段。Oracle就是通过CHANGE_VECTOR$$
列来记录每条记录发生变化的字段包括哪些。- 如果WITH后面跟了
ROWID
,则物化视图日志中会包含:M_ROW$$
:用来存储发生变化的记录的ROWID。 - 如果WITH后面跟了
PRIMARY KEY
,则物化视图日志中会包含主键列。 - 如果WITH后面跟了
OBJECT ID
,则物化视图日志中会包含:SYS_NC_OID$
:用来记录每个变化对象的对象ID。 - 如果WITH后面跟了
SEQUENCE
,则物化视图日子中会包含:SEQUENCE$$
:给每个操作一个SEQUENCE号,从而保证刷新时按照顺序进行刷新。 - 如果WITH后面跟了一个或多个COLUMN名称,则物化视图日志中会包含这些列。
演示创建物化视图with primary key
我们首先创建一张表
create table test_person(pid int,name varchar2(20));
alter table test_person add constraint test_pk_pid primary key(pid);
然后我们创建物化视图日志,指定primary key
create materialized view log on test_person with primary key;
然后我们可以向基表test_person加入一条数据。
insert into test_person values(1,'kevin');
然后在update一条数据
update test_person set name='kevin cai' where pid = 1;
然后我们通过如下sql可以查看物化视图日志的内容。
select * from mlog$_test_person;
including new values
我们创建物化视图日志的时候还可以带上including new values
create materialized view log on test_person with primary key including new values;
同样我们也是先insert一条数据然后在update同一条数据,然后我们从mlog$_test_person中的内容就可以看得区别。
NEW VALUES子句允许Oracle数据库将新旧值都保存在物化视图日志中,以便更新DML操作。
更多参考:
http://www.itpub.net/forum.php?mod=viewthread&tid=2052180&highlight=
https://docs.oracle.com/cd/E11882_01/server.112/e41084/statements_6003.htm#i2119924
演示创建物化视图with rowid
CREATE MATERIALIZED VIEW LOG on test_person with rowid INCLUDING NEW VALUES;
使用场景,具体不是很了解,可以参考:https://blog.csdn.net/demonson/article/details/81450938
实现物化视图的增量刷新
我们先创建两张表
create table test_person(pid int,name varchar2(20));
alter table test_person add constraint test_pk_pid primary key(pid);
create table test_address(aid int,address varchar2(20),pid int);
alter table test_address add constraint test_pk_aid primary key(aid);
然后我们对基表创建物化视图日志
我们这里使用的是with rowid来创建物化视图日志
CREATE MATERIALIZED VIEW LOG on test_person with rowid INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW LOG on test_address with rowid INCLUDING NEW VALUES;
然后我们就可以创建fast刷新模式的物化视图。。
create materialized view my_test_mv refresh fast WITH ROWID asselect p.pid as p_id, p.name, a.address, p.ROWID as p_ROWID,a.ROWID as a_ROWIDfrom test_person p, test_address a where p.pid = a.pid;
注意:
with rowid的时候记得创建物化视图的时候需要带上select条件ROWID,不然会宝如下错误:
ORA-12052: cannot fast refresh materialized view
而且建MV时一定要加上with rowid,因为没有聚合函数的MV默认是with primary key
更多参考:https://blog.csdn.net/minwang593/article/details/19205553
这时候我们可以尝试往基表插入数据
insert into test_person values(1,'kevin');
insert into test_address values(1,'SHA',1);
insert into test_person values(2,'vincent');
insert into test_address values(2,'HKG',2);
我们可以看到物化视图日志中都有数据
select * from mlog$_test_person;
select * from mlog$_test_address;
但是物化视图中还没有数据。我们可以通过手动方式刷新物化视图中的数据
BEGIN
DBMS_MVIEW.REFRESH (
list => 'my_test_mv',
Method =>'C',
refresh_after_errors => True);
END;
之后就可以发现物化视图中有数据了。
而且一旦物化视图刷新,关联基表的物化视图日志也会被清空。。
我们可以通过如下sql删除物化视图和物化视图日志:
--删除物化视图
drop materialized view my_test_mv;
--删除物化视图日志
drop materialized view log on test_person;
drop materialized view log on test_address;
然后我们可以试试以外连接的语法来创建物化视图
create materialized view my_test_mv refresh fast WITH ROWID asselect p.pid as p_id, p.name, a.address, p.ROWID as p_ROWID,a.ROWID as a_ROWIDfrom test_person p left join test_address a on p.pid = a.pid;
然后我们发现我们创建物化视图的时候报错如下:
[72000][12015] ORA-12015: cannot create a fast refresh materialized view from a complex query
这是因为物化视图快速刷新不支持标准外联接写法,必须要改写sql
select * from a,b where a.id(+)=b.id;--b为主表,a为补充
select * from a,b where a.id=b.id(+);--a为主表,b为补充
所以我们将我们的sql改写为
create materialized view my_test_mv refresh fast WITH ROWID asselect p.pid as p_id, p.name, a.address, p.ROWID as p_ROWID,a.ROWID as a_ROWIDfrom test_person p, test_address a where p.pid = a.pid(+);
更多参考:https://www.cnblogs.com/tracy/archive/2011/09/01/2162080.html
总结一下创建fast模式物化视图条件:
多表连接的物化视图想要使用fast刷新时,
首先,select 语句中包含到的每一个表都需要创建视图日志;
其次,视图日志中需要指定在select语句和where条件中用到的该表的字段;
第三,在select中必须包含所涉及到的所有表的rowid,
第四,外连接不能使用标志sql语法,应使用(+)这种oracle固有语法.
当然还有很多其他的限制条件,可以参考:https://blog.csdn.net/aiyocxuan/article/details/78732567
使用物化视图增量刷新的优缺点
我个人觉得使用增量模式刷新物化视图的优点就是不需要对整个物化视图进行刷新,当物化视图的数据特别大的时候可以减少刷新的时间,其他的优点没有暂时还没感受到。
缺点:
1.使用增量模式进行刷新需要对所有关联的基表都建立物化视图日志,首先在数据库中就要占据一部分的空间,其次对基表创建物化视图日志对基表的DML还是有影响的,可以参考下面:
Oracle 物化视图快速刷新对性能的影响
http://blog.itpub.net/28539951/viewspace-2127870/
2.然后对基表的DML和对物化视图日志的更新应该是在同一个事务里的,因为rollback的时候是会一起rollback的,所以如果物化视图日志一旦失败可能导致基表的DML操作失败,对业务上可能会影响。
3.物化视图日志的维护成本比较高
物化视图日志经常会由于物化视图长时间没有刷新,或者基表的一次批量数据更改而变得很大,这会影响物化视图的刷新性能,因此对于这种情况需要对物化视图日志进行处理,降低物化视图日志表的高水位线。
Oracle的物化视图的快速刷新功能,主要是靠物化视图日志来实现的。
物化视图日志会记录下基表所有的增、删、改操作,而物化视图执行完快速刷新操作后,会从物化视图日志中将本物化视图刷新过且其他物化视图所不需要刷新的记录删除掉。如果其中一个物化视图一直不刷新,那么物化视图日志就会变得越来越大。
还有一种情况,比如表中插入了大量的数据,或者删除了大量的数据,或者将表中的某一列统一更新为一个值,这种操作都会在物化视图日志中产生大量的记录。
而物化视图日志的增大必然影响物化视图的刷新速度。一方面,物化视图在刷新的时候要扫描物化视图日志,另一方面,物化视图在刷新结束后,也要清除物化视图日志中的记录,仍然要扫描物化视图日志,因此物化视图日志的大小直接会影响物化视图快速刷新的速度。更重要的是,物化视图日志的高水位一旦增长到一个很高的位置,即使以后物化视图日志中记录很少,甚至没有记录存在,物化视图在刷新的时候仍然需要较长的时间。
oracle通过v$sql视图查询sql的平均执行时间
SELECT SQL_TEXT,EXECUTIONS "总执行次数",ELAPSED_TIME/1000 "总耗时(秒)", ELAPSED_TIME/nvl(EXECUTIONS,1)/1000 "平均耗时(秒)",PARSE_CALLS "硬解析次数",DISK_READS "物理读次数",BUFFER_GETS "读缓存区次数"
FROM v$SQL WHERE SQL_TEXT LIKE '%select * from t1%';
更多参考:https://blog.csdn.net/yh_zeng2/article/details/78946807
Oracle物化视图与物化视图日志相关推荐
- Oracle 如何根据物化视图日志快速刷新物化视图 (不积跬步,无以至千里)
Oracle物化视图的快速刷新机制是通过物化视图日志完成的.Oracle如何通过一个物化视图日志就可以支持多个物化视图的快速刷新呢,本文简单的描述一下刷新的原理. 首先,看一下物化视图的结构: SQL ...
- oracle雾化试图_Oracle 物化视图 说明
一. 物化视图概述 Oracle的物化视图是包括一个查询结果的数据库对像,它是远程数据的的本地副本,或者用来生成基于数据表求和的汇总表.物化视图存储基于远程表的数据,也可以称为快照. 物化视图可 ...
- Oracle物化视图和普通视图区别
一.物化视图的简介 物化视图是一种特殊的物理表,"物化"(Materialized)视图是相对普通视图而言的.普通视图是虚拟表,应用的局限性大,任何对视图的查询,Oracle都实际 ...
- Oracle数据库的视图、物化视图、序列、同义词、索引
Oracle数据库对象 视图 物化视图 序列 同义词 索引 注:以下数据库对象中, 物化视图.序列.同义词为Oracle数据库特有 视图 含义:视图是一种数据库对象,是从一个或者多个数据表或视图中导出 ...
- oracle雾化试图_ORACLE物化视图具体解释
一.物化的一般使用方法物化视图是一种特殊的物理表,"物化"(Materialized)视图是相对普通视图而言的.普通视图是虚拟表,应用的局限性大,不论什么对视图的查询,oracle ...
- oracle雾化试图_Oracle物化视图语法
物化视图概述: Oracle的物化视图提供了强大的功能,可以用在不同的环境中.在不同的环境中,物化视图的作用也不相同.数据仓库中的物化视图主要用于预先计算并保存表连接或聚集等耗时较多的操作的结果,这样 ...
- 傅老师课堂:Oracle高级应用之物化视图(materialized view)
原文地址:http://hi.baidu.com/gukeming888/blog/item/2682f69481c8237154fb9662.html 物化视图 (Materialized View ...
- oracle物化视图和表的区别,数据库中普通视图和物化视图有什么区别?
对于增量刷新选项,如果在子查询中存在分析函数,则物化视图不起作用. Refresh方法- COMPLETE子句 完全刷新重新生成整个视图,如果请求完全刷新,oracle会完成 完全刷新即使增量刷新可用 ...
- 物化视图VS普通视图
2019独角兽企业重金招聘Python工程师标准>>> 物化视图是一种特殊的物理表,"物化"(Materialized)视图是相对普通视图而言的.普通视图是虚拟表 ...
最新文章
- 深度学习-tensorflow1.x之交叉熵损失函数(softmax_cross_entropy_with_logits)代码实现 Tensorflow1.x 和 Numpy
- 使用performance monitor 查看 每一个cpu core的cpu time
- java setmessage_Java Message.setTitle方法代码示例
- scrapy.request
- linux上的MySQL默认端口,linux下mysql 查看默认端口号与修改端口号方法
- C语言2019软件,c语言模拟编程学习软件v2019 最新版
- 基于内容的图像检索系统设计与实现
- 2018年春季学期《软件工程》班级讨论群中开放性问题群聊记录
- 《中国通史》学习记录
- 北京地铁线路色值颜色
- 查看获取别人的微信公众号二维码
- 【githubshare】深度学习蘑菇书,覆盖了强化学习、马尔可夫决策过程、策略梯度、模仿学习
- javaScript笔记宝典
- 计算机安装硬盘后无法启动不了,双硬盘无法启动提示"invalid partition table"开不了机怎么解决?...
- LSTM长短期记忆网络
- Atitit 最近资料文章列表r9 r8 月份 attilax总结
- 51cto——让梦飞翔
- SEO魔法书-网站优化
- moment获取时间
- 以太坊外无Defi?EOS:我有
热门文章
- CE认证和CCC认证区别?
- S3 #DooTrader 经典组冠军以良好盘感,创下近 900% 收益率摘得桂冠
- 《Python 黑帽子》学习笔记 - Python3 实现 netcat - Day 8
- ps2键盘测试软件,stm32_ps2键盘显示测试程序
- KD树详解及KD树最近邻算法
- Unity 之 关于停止协程的五种方式解析
- Ceph RBD:条带(stripe)详解
- 编程语言-2-处理器架构、指令集和汇编语言
- 机器学习:SVM支持向量机理解
- php发送邮件封装类,使用nette/mail 封装一个发送邮件类 (通用)