本文介绍一种数据库并发机制表格分析方法。借助表格,能够帮助理解、分析 DB2 的并发机制,并能对 DB2如何实现这种并发机制窥探一二。我们还将借助一些实例,来进行概念的验证和对理论更加深刻的理解。
<!----><!---->
<!---->
基本概念
对于数据库初学者来说,深刻理解 DB2 里面的并发机制并不是一件很容易的事。事务、隔离级别、锁、锁兼容这些概念读起来十分晦涩,这些概念之间的关系似乎也是若隐若现,令人琢磨不透。
这里我们无意把所有的数据库并发机制相关概念再复述一遍,这不是本文的重点,但是很有必要在这里对一些重要的概念稍微复习一下,包括事务,隔离级别,锁,锁兼容等。对这些概念有大概的了解会对您阅读本文大有帮助。
事务
事
务在 DB2
中通常称为工作单元。工作单元是应用程序进程内可恢复的操作序列。数据库管理器使用它来确保数据库处于一致状态。对数据库的任何读取或写入都在一个工作单
元内执行。当对数据库发出第一条 SQL
语句时,就隐式启动了一个工作单元。由同一应用程序执行的所有后续读写操作被认为是同一工作单元的一部分,直到应用程序发出 COMMIT 或
ROLLBACK 语句来结束该工作单元。
隔离级别
隔离级别确定访问数据时如何锁定或者隔离该数据,以使其不受其他进程的影响。除非显式地进行更改,否则隔离级别在工作单元的运行期间保持有效。
由于多个用户访问和更改数据,所以必须维护关系数据库数据完整性。并发性指的是多个交互式用户或应用程序可以同时共享资源。隔离级别指定:
应用程序读取和更新的行可供其他并发执行的应用程序进程使用的程度;以及应用程序受其他并发执行的应用程序进程的更新活动的影响程度。
DB2 支持下列隔离级别:
- 可
重复读 (Uncommitted Read -
UR):在工作单元内,应用程序读取的所有行都被锁定。因此,当游标处于打开状态时,在同一工作单元内发出两次的查询将返回相同的结果。在该工作单元完成
之前,其他应用程序无法更新、删除或插入可能会影响结果表的行。“可重复读”应用程序看不到其他应用程序的未落实更改。
- 读
稳定性 (Read Stability -
RS):读稳定性只锁定应用程序在工作单元内检索的那些行。它确保在某个工作单元完成之前,在该工作单元运行期间的任何限定行读取不被其他应用程序进程更
改,且确保不会读取由另一个应用程序进程所更改的任何行,直至该进程落实了这些更改。
- 游标稳
定性 (Cursor Stability -
CS):对于事务访问的行,只要游标定位在该行上,就锁定该行。此锁定在访存下一行或事务终止之前一直有效。但是,如果更改了某一行上的任何数据,则在对
数据库落实更改之前必须挂起该锁定。因此,在同一个工作单元内发出了两次的查询可能不会返回相同的结果。
- 未落实的读 (Uncommitted Read - UR) :应用程序可以访问其他事务的未落实更改。除非其他应用程序尝试删除或改变该表,否则该应用程序也不会锁定正读取的行而使其他应用程序不能访问该行。此隔离级别通常用于对只读表执行的查询。
锁
要提供并发性控制并防止无控制的数据访问,数据库管理器将锁定置于缓冲池、表、数据分区、表块或表行上。锁定将数据库管理器资源与应用程序关联(称为锁定所有者),以控制其他应用程序访问同一资源的方式。
锁的基本属性包括:object, size, duration, mode。如果您对这些属性所表达的含义不是特别清楚,建议您先读一下相关文章和 DB2 infocenter 里相关章节。我们在这里只是罗列出本文所需要的必要的信息。
锁的持续时间会根据隔离级别而有所不同:
- UR 扫描:除非行数据正在进行更改,否则不挂起行锁定。
- CS 扫描:仅当游标定位在行上时,才挂起行锁定。
- RS 扫描:事务期间只挂起符合查询条件的行锁定。
- RR 扫描:事务期间挂起所有行锁定。
锁状态(mode)
- IN: 无意向
- IS: 意向共享
- NS: 下一键共享
- S: 共享
- IX: 意向互斥
- SIX:在意向互斥下共享
- U: 更新
- NW: 下一键弱互斥
- X: 互斥
- W: 弱互斥
- Z: 超级互斥
锁
兼容:在一个应用程序当前拥有对某个对象的锁定,而另一个应用程序请求对同一个对象的锁定时,就会出现锁定兼容性问题。当两种锁定方式兼容时,可以同意对
该对象的第二个锁定请求。如果请求的锁定的锁定方式与已挂起的锁定不兼容,则不能同意锁定请求。相反,请求要等到第一个应用程序释放其锁定,并且释放所有
其他现有不兼容锁定为止。
表格分析法
也许读完上面的一些概念,还是会有许多疑问,诸如在不同的隔离级别下做不同的操作前需要满足那些条件,以及操作完成后对其他事务到底会产生什么影响?除了从文字上进行推敲,有没有其他的办法帮助我们分析和判断 DB2 的并行机制?
现在就让我们进入本文主题。本文将介绍一种表格分析法,借助两种表格,可以对使用不同隔离级别的事务进行不同操作时的行为进行精确分析和判断。
这里我们要引入两类特别重要的表,一张表是用来描述使用不同隔离级别在做不同的操所时需要获取的锁类型,另一张表描述的是锁类型的兼容性。
事务在不同的隔离级别中进行不同的操作,获取的锁类型也不同。比如在最高隔离级别 RR 中修改数据获取的锁 (S) 与 CS 级别中读数据获取的锁(NS) 就不一样。
另外访问方案也可对锁定方式具有很大影响。举例来说,当使用索引扫描来查找特定行时,优化器将可能为该表选择行级别锁定(IS);而如果不使用索引,则必须按顺序扫描整个表来找到所选的行,并且可因此获取单个表级锁定(S)。
因此获取的锁定的类型依赖于隔离级别、操作类型,以及访问方案诸多因素。为了简单起见,我们只对标准表,使用索引和全部扫描的访问方案来做分析。本文没有涉及集群表中的锁定以及被延迟的数据页访问方式。
表1描述了在不使用谓词 ( SQL 语句里的 where 部分) 进行表扫描的访问方式下,在不同隔离级别下做各种操作所要获取的锁定方式。
表 1. 不使用谓词的表扫描的锁定方式
隔离级别
只读和模糊扫描
游标-扫描
游标-当前行
更新或删除-扫描
更新或删除-更新行
RR
S/-- |
U/-- |
SIX/X |
X/-- |
X/-- |
RS
IS/NS |
IX/U |
IX/X |
IX/X |
IX/X |
CS
IS/NS |
IX/U |
IX/X |
IX/X |
IX/X |
UR
IN/-- |
IX/U |
IX/X |
IX/X |
IX/X |
表2描述了在使用谓词 ( SQL 语句里的 where 部分) 进行表扫描的访问方式下,在不同隔离级别下做各种操作所要获取的锁定方式。
表 2. 使用谓词的表扫描的锁定方式
隔离级别
只读和模糊扫描
游标-扫描
游标-当前行
更新或删除-扫描
更新或删除-更新行
RR
S/-- |
U/-- |
SIX/X |
U/-- |
SIX/X |
RS
IS/NS |
IX/U |
IX/X |
IX/U |
IX/X |
CS
IS/NS |
IX/U |
IX/X |
IX/U |
IX/X |
UR
IN/-- |
IX/U |
IX/X |
IX/U |
IX/X |
表3描述了在使用具有唯一性的索引来进行索引扫描的访问方式下,在不同隔离级别下做各种操作所要获取的锁定方式。
表 3. 使用单个合格行的 RID 索引扫描的锁定方式
隔离级别
只读和模糊扫描
游标-扫描
游标-当前行
更新或删除-扫描
更新或删除-更新行
RR
IS/S |
IX/U |
IX/X |
IX/X |
IX/X |
RS
IS/NS |
IX/U |
IX/X |
IX/X |
IX/X |
CS
IS/NS |
IX/U |
IX/X |
IX/X |
IX/X |
UR
IN/-- |
IX/U |
IX/X |
IX/X |
IX/X |
在上面两张表中,每一项由两部分组成: 表锁定和行锁定,横线代表没有该对象上的锁定。
表4显示锁定之间的兼容关系。Y 表示锁定兼容,即当一个进程挂起或正在请求对同一个资源的锁定时,可以同意锁定请求。N 表示锁定不兼容,请求者必须等待,直到所有不兼容的锁定被其他进程释放为止。
表 4. 锁定类型兼容性
无
IN
IS
NS
S
IX
SIX
U
X
Z
NW
W
无
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
IN
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
N |
Y |
Y |
IS
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
N |
N |
N |
N |
NS
Y |
Y |
Y |
Y |
Y |
N |
N |
Y |
N |
N |
Y |
N |
S
Y |
Y |
Y |
Y |
Y |
N |
N |
Y |
N |
N |
N |
N |
IX
Y |
Y |
Y |
N |
N |
Y |
N |
N |
N |
N |
N |
N |
SIX
Y |
Y |
Y |
N |
N |
N |
N |
N |
N |
N |
N |
N |
U
Y |
Y |
Y |
Y |
Y |
N |
N |
N |
N |
N |
N |
N |
X
Y |
Y |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
Z
Y |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
N |
NW
Y |
Y |
N |
Y |
N |
N |
N |
N |
N |
N |
N |
Y |
W
Y |
Y |
N |
N |
N |
N |
N |
N |
N |
N |
Y |
N |
下面我们通过一个实例来看一个如何根据这些表格来分析 DB2 中的锁机制。首先需要准备试验环境。
这里我们使用了 DB2 V9,实际上这些试验同样适用于 DB2 V8。
首先运行 db2cmd,初始化 DB2 CLP (命令行处理器)的运行环境,然后输入 db2,进入命令行处理器。创建数据库,连接到数据库,创建表格,以及插入两条试验数据,具体操作如 listing1:
清单 1. 创建数据库和表
db2 = > CREATE DATABASE TESTDB USING CODESET UTF-8 TERRITORY US COLLATE USING SYSTEM
DB20000I CREATE DATABASE命令成功完成。
db2 = > connect to TESTDB
数据库连接信息
数据库服务器 = DB2/NT 9.1.0
SQL 授权标识 = QKK
本地数据库别名 = TESTDB
db2 = > create table test (id varchar(2) not null, name varchar(20), primary key(id))
DB20000I SQL命令成功完成。
db2 = > insert into test values('1', 'a')
DB20000I SQL命令成功完成。
db2 = > insert into test values('2', 'b')
DB20000I SQL命令成功完成。
|
然
后我们使用两个 CLP 作为两个独立的进程,分别运行事务,来观察和分析 DB2 中的锁机制。两次运行 db2cmd,打开两个 DB2
的命令运行环境,然后输入 db2 +c,进入命令行处理器。加参数 +c 是为了保证在 DB2 CLP 中的 SQL
语句不自动提交,这种方式下,只有输入命令 commit 或者 rollback 才会结束一个事务。
在第一个 CLP 中运行
清单 2. 事务处理一
db2 +c
db2 => connect reset
DB20000I SQL 命令成功完成。
db2 => change isolation to RS
DB21053W 连接不支持 RS 的数据库时会进行自动升级。
DB20000I CHANGE ISOLATION 命令成功完成。
db2 => connect to testdb
数据库连接信息
数据库服务器 = DB2/NT 9.1.0
SQL 授权标识 = QKK
本地数据库别名 = TESTDB
db2 => select * from test
ID NAME
-- --------------------
1 a
2 b
2 条记录已选择。
db2 => update test set name='abc' where name='a'
DB20000I SQL 命令成功完成。
|
我们设定了事务的隔离级别为读稳定 (RS),并且修改了一条数据。注意这里没有 commit,因为我们不想结束这个事务。
在第二个 CLP 中,运行
清单 3. 事务处理二
db2 +c
db2 => connect reset
DB20000I SQL 命令成功完成。
db2 => change isolation to RS
DB21053W 连接不支持 RS 的数据库时会进行自动升级。
DB20000I CHANGE ISOLATION 命令成功完成。
db2 => connect to testdb
数据库连接信息
数据库服务器 = DB2/NT 9.1.0
SQL 授权标识 = QKK
本地数据库别名 = TESTDB
db2 => select * from test where id='2'
ID NAME
-- --------------------
2 b
1 条记录已选择。
db2 =>select * from test where id='1'
|
在第二个事务中,我们会发现能够读取出 id='2' 的数据,但是当查询 id='1' 的数据时,没有数据输出,说明发生了锁等待。
下面我们来分析一下:事务一执行 update 操作时,因为在 where 语句中使用的是name='a'
,没有包括键值,所以数据库管理器在进行操作时会进行全表扫描。
我们就应该看使用谓词的表扫描的锁定方式表格,从表 2 中可以判断,在 RS 级别下,更新的行(被修改的第一行)加 X 锁,更新-扫描行(第二行)加 U 锁。
事务二在执行select * from test where id='2'
时,where 语句中使用主键值,对于主键是建有索引的,因此根据表 3 (唯一匹配的索引扫描锁定表)来判断需要获取的锁模式)。
从表中可以看出,RS 级别下被扫描的行(只有第二行)需要申请 NS 锁。第二行已经被事务一加了 U 锁,那么事务二能否获取到 NS 锁呢,这就要看 U 锁和 NS 锁是否兼容。
这时需要查看锁的兼容表。从表4中可以看出,U 和 NS 是兼容的,因此事务二可以获取锁,能够完成查询操作。
当事务二执行select * from test where id='1'
时,从表 3 中查出,被扫描的第一行需要 NS 锁,与事务一加的 X 锁不兼容,因此发生锁等待。
这时只有第一个事务运行 commit 或者 rollback,事务一结束,X 锁被释放,第二个事务才可以获取到 NS 锁,得到查询结果。
那么如果在事务二中运行select * from test where name='b'
呢?这条语句是要查询的第二条记录,需要的 NS 锁与事务一在这条记录上的U锁是兼容的,应该不会发生锁等待,可以得到结果。事实是不是这样呢?
做一下试验就会发现,也是需要等待的,为什么呢?仔细想想就会发现,语句select * from test where name='b'
并没有使用索引,而是做全表扫描,从 table 1 中可以得到,在隔离级别为 RS 的事务中,所有被扫描的行都需要 NS 锁,包括第一行,与第一行记录已经有的 X 锁是不兼容的,因此当扫描到第一行时就需要锁等待了。
试验
热身完毕,我们已经基本了解如何使用获取锁表和锁兼容表来分析事务的行为了。下面我们针对各个隔离级别分别设计一些试验来理解隔离级别的意义。
未提交的读隔离级别 (UR) 是最不严格的隔离级别。实际上,在使用这个隔离级别时,仅当另一个事务试图删除或更改被检索的行所在的表时,才会锁定一个事务检索的行。
因为在使用这种隔离级别时,行通常保持未锁定状态,所以脏读、不可重复的读和幻像都可能会发生。下面我们就设计几个试验看如何发生的脏读,不可重复的读以及幻像。
试验一:UR下的脏读
当事务读取尚未提交的数据时,就会发生脏读。例如:事务 1 更改了一行数据,而事务 2 在事务 1 提交更改之前读取了已更改的行。如果事务 1 回滚该更改,则事务 2 就会读取被认为是不曾存在的数据。我们用试验来感受一下。
首先在事务一中执行connect reset; change isolation to UR; connect to testdb
来把事务的隔离级别修改为UR。
第二个事务可以采用任何隔离级别,只需要保证在第二个窗口中采用不自动提交方式进入 CLP。在第二个事务中运行 update test set name='abc' where id='01'
,
然后在第一个事务中运行 select * from test
。这里读取的数据是第二个事务已经修改过的,但是还没有提交的数据。在第二个事务运行 rollback,取消刚才的修改,
在第一个事务运行 select * from test
,发现读取的数据又变成了修改前的数据。
图 1. UR 下的脏读(事务一)
图 2. UR 下的脏读(事务二)

事务一的第一次读,读到的就是脏数据。对照表格稍微分析一下就可以得知,发生脏读原因就是 UR 事务读操作的时候不对行进行的锁定,这样一方面事务本身读数据时不受约束,同时由于不对数据进行锁定,那么使得其他事务修改时也就不受 UR 事务的约束了。
游标稳定性隔离级别在隔离事务效果方面非常宽松。它可以防止脏读;但有可能出现不可重复的读和幻像。
试验二:CS 下如何防止脏读
把事务一的隔离级别改为 CS,然后重复上面的试验,会发现在事务二修改数据以后,在事务二提交以前,事务一去读修改的数据时会发生锁等待,直到事务二 commit 或者 rollback,因此不会发生脏读。试验过程如下图所示:
图 3. CS 下防止脏读(事务一)
图 4. CS 下防止脏读(事务二)

还是利用我们的表格分析法,从表中可以看出,无论采用什么访问方式,在 CS 隔离级别下,读取数据时需要在行上加 NS 锁,如果此时数据上己经有其他事务加了X锁(被修改时加X锁),由于 NS 锁与 X 锁的不兼容,使得读数据的时候一定发生锁等待。
试验三:CS 下不可重复的读
CS
可以防止脏读,但是可能出现不可重复的读和幻像。如下图试验所示:事务一读完数据后,事务二修改数据(或者添加数据),并且提交,那么事务一再去读数据,
会发生同一条记录跟上一次读的结果不一样,这就是不可重复的读(也会发生读出上一次没有查询到的记录,这就是幻像)。
图 5. CS 下不可重复的读(事务一)
图 6. CS 下不可重复的读(事务二)

但
是如果我们仔细按照表格分析就会产生这样的疑问:当第一个事务读取数据以后,按照表格1,数据应该加 NS
锁,事务二在修改数据的时候,需要在被修改数据行上加X锁,表 4 中看出,X 锁与 NS
锁不兼容,事务二应该是锁等待阿,然而情况并非如此。从试验中可以看出,事务二在修改数据时没有等待。 不过这确实符合 CS 隔离级别的描述,CS
级别下,只读取已经提交了数据,但是读取并不会影响其他事务对数据的修改。但是与表格分析得出的结果矛盾,原因在哪儿呢? 我们可以借助 DB2
活动监视器来分析一下。我们先来复现一下事务一读取数据以后的状态。首先在通过 commit 来结束事务一,然后重新执行 select
语句以恢复到读取后的状态。 打开 DB2 活动监控器,选择数据库 testdb,然后在检视任务中选择正在解决应用程序锁定状态,点击完"成看"。
在报告栏中选择"具有最大锁定超时数的应用程序",找到第一个 CLP 对应的程序,右击,选择显示锁定链,如下图:
图 7. 显示锁定链

在应用程序锁定链窗口中,右击程序,选择显示锁定详细信息。
图 8. 显示锁定详细信息

在详细信息窗口中,会列出此程序已经拥有的锁定和等待的锁定。在这里面,我们并没有发现针对行对象的 NS 锁。
我们分别在 CS 级别和 RS 级别下做相同的查询操作,然后比较它们的锁定状况,就会更清楚地发现它们的不同,尽管在表 1 和表 2 里 CS 和 RS 看起来是一样的。
图 9. CS 级别锁定状况
图 10. RS 级别锁定状况

什么原因呢?是不是表 1 和表 2 把锁模式弄错了,CS 隔离级别下读操作也不需要 NS 锁?应该也不是,因为如果我们让事务二先修改数据,再让事务一去读,就回发生锁等待,因此事务一读数据时,还是需要行的 NS 锁,否则象 UR 级别一样,也会发生脏读。
回头看一下我们在基本概念一节中的这样一段话:
锁的持续时间会根据隔离级别而有所不同:
也就是说在只有游标操作方式的时候,才会在当前行加锁,象我们所做的只读操作,不会持续占用锁,读操作完成后锁就会被释放掉,不会等到事务结束,这样就可以解释在 CS 级别下,为什么对数据的读取不会阻塞其他事务对该数据的修改了。
读稳定性隔离级别:读稳定性隔离级别可以防止脏读和不可重复的读,但是可能出现幻像。在使用这个隔离级别时,只锁定事务实际检索和修改的行。
试验四:RS 下防止不可重复的读
将事务一的隔离级别改为 RS,然后执行select * from test where id='1'
读取数据;
事务二执行update test set name='abc' where id='1'
来修改数据,会发现事务二发生锁等待。
事实上,无论第二个进程使用何种隔离级别,现象是一样的。还是用锁兼容表分析一下。在事务一读数据时,因为是索引扫描,因此从表 3 中得出,只是在被检索的数据上加 NS 锁(不同于 CS,锁会持续到事务结束),
事务二在做修改的时候,获取 X 锁,与 NS 不兼容,因此锁等待,直至事务一结束。这样就保证了在事务一的操作过程中,被检索的行不会被其他进程修改,每次读到的同一条记录都不会有变化,从而保证了读稳定。
图 11. RS 下防止不可重复的读(事务一)
图 12. RS 下防止不可重复的读(事务二)

试验五:RS 下的幻像
那么如何发生的幻像呢?事务一读取数据后,在事务二中插入一条数据insert into test values('3', 'c')
并且提交,
再在事务一中执行select * from test
, 就会发现与前一次 select 结果相比,多了一条数据,这就是幻像。
图 13. RS 下的幻像(事务一)
图 14. RS 下的幻像(事务二)

可重复读隔离级别:可重复读隔离级别是最严格的隔离级别。在使用它时,一个事务的影响完全与其他并发事务隔离:脏读、不可重复的读、幻像都不会发生。我们现在就来验证一下。
试验六:RR 下防止幻像
事务一隔离级别改为 RR,然后执行select * from test
,
事务二执行insert into test values('4', 'd')
,会发现事务二发生锁等待。
还是用表格分析法,事务一读取数据时,从表1可以看出,在表上加了 S 锁,事务二插入数据时需要在表上申请 X 锁,X 与 S 不兼容,因此进程二只能锁等待。
图 15. RR 下防止幻像(事务一)
图 16. RR 下防止幻像(事务二)

这样我们就会很自然地发现,隔离级别越高,操作越安全,但是发生锁等待的机会就越大,效率会降低,甚至会发生死锁。下面我们再来设计一个死锁的例子。
两个进程中的事务都采用 RS (读稳定)的隔离级别,事务一执行select * from test where id='1'
,
事务二执行select * from test where id='2'
,
事务一执行update test set name='bb' where id='2'
,
事务二执行update test set name='bb' where id='1'
。
这种情况下,就会发生事务一在执行 update 的时候等待事务二的 NS 锁被释放,而事务二执行 update 的时候也会等待事务一的 NS 锁别释放,这样就发生了死锁。
好在 DB2 能进行死锁检测,当发现死锁时,会中断并回滚其中一个事务,另一个事务就可以继续了。
图 17. RR 下防止幻像(事务三)
图 18. RR 下防止幻像(事务四)
总结
我们已经通过试验的方法,掌握了如何运用表格来分析和判断事务在不同的隔离级别下,执行不同的操作时的行为,从而能够更加深刻理解 DB2 的并发机制。
您可以用这种方法来对您的数据库并发操作方案进行分析和评估,来判断是否满足实际需求。但是要指定好的并发操作方案,还是需要对数据库的并发机制有着融会贯通的理解。
参考资料
作者简介
|
|
|
齐克科 2001年加入IBM,目前就职于中国开发中心,从事MBPS(Managed Business Process Services)相关的工作。
|
|
|
|
Elaine Zhan 中国开发中心 MBPS develope lead,负责CSDP common service 和 provisioning 的开发工作。
|
|
|
|
陈荔龙 2004年加入IBM,目前就职于中国开发中心,从事MBPS(Managed Business Process Services)相关的工作。
|
分享到:
相关推荐
层次模型采用树形结构,网状模型用有向图表示,而关系模型则以二维表格为基本结构,如SQL Server 2000、Oracle、DB2和Sybase。关系模型能够描述一对一和一对多的关系,而网状模型则可以表示更复杂的一对多和多对多...
- 数值数据表示:原码、反码、补码表示法。 - 校验码:奇偶校验码、CRC校验码等。 - 逻辑运算:AND、OR、NOT等基本逻辑运算符。 #### 二、办公软件的应用 1. **文字处理软件Word2002** - 功能:文档编辑、格式...
这个数据集提供了2010年至2021年间加拿大各省的家庭支出与收入数据,这些数据根据人口统计和地理指标进行了分类。每行代表了年份(REF_DATE)、省份(GEO)以及编码后的支出或收入类型的唯一组合(COORDINATE)。以下是该数据集的关键特点及包含的列信息: 关键特点: 支出数据:家庭支出按照收入五分位数和支出类别进行分类。 收入数据:家庭收入值根据家庭类型、较年长成年人的年龄组别和收入水平细分。 地理位置匿名化:为了保护隐私,原始的地理位置标识符被替换为如“Province 1”这样的标签。 时间序列:涵盖了超过十年的财务数据(2010–2021),适合用于纵向经济和社会趋势分析。 包含的列: REF_DATE:记录年份(2010–2021) GEO:省份标签(例如,“Province 1”) Statistic:度量类型(例如,平均家庭支出) Before-tax household income quintile:税前家庭收入水平分组 Household expenditures, summary-level categories:支出类别 UOM:计量单位 COORD
1.【锂电池剩余寿命预测】GRU门控循环单元锂电池剩余寿命预测(Matlab完整源码和数据) 2.数据集:NASA数据集,已经处理好,B0005电池训练、测试; 3.环境准备:Matlab2023b,可读性强; 4.模型描述:GRU门控循环单元在各种各样的问题上表现非常出色,现在被广泛使用。 5.领域描述:近年来,随着锂离子电池的能量密度、功率密度逐渐提升,其安全性能与剩余使用寿命预测变得愈发重要。本代码实现了GRU门控循环单元在该领域的应用。 6.作者介绍:机器学习之心,博客专家认证,机器学习领域创作者,2023博客之星TOP50,主做机器学习和深度学习时序、回归、分类、聚类和降维等程序设计和案例分析,文章底部有博主联系方式。从事Matlab、Python算法仿真工作8年,更多仿真源码、数据集定制私信。
2000-2024年各省专利侵权案件结案数数据 1、时间:2000-2024年 2、来源:国家知识产权J 3、指标:专利侵权案件结案数 4、范围:31省 5、用途:可用于衡量知识产权保护水平
- 使用`<div>` 容器组织游戏界面,包含得分显示、游戏画布和操作按钮 - 支持三种游戏模式选择(一般模式、困难模式、无敌模式) - 移动端和桌面端兼容,提供触摸和键盘两种控制方式 2. CSS样式 : - 采用Flex布局实现页面居中显示 - 使用Grid布局实现方向按钮的排列 - 定义了游戏容器的阴影、圆角等视觉效果 - 为按钮添加了hover效果和过渡动画 3. JavaScript逻辑 : - 使用Canvas API实现游戏渲染 - 实现了蛇的移动、食物生成、碰撞检测等核心游戏逻辑 - 支持三种游戏模式,不同模式对应不同的游戏速度和规则 - 使用localStorage保存最高分记录 - 实现随机颜色生成,使游戏更具趣味性 代码整体结构清晰,功能完整,具有良好的可扩展性和可维护性。
台区终端电科院送检文档
内容概要:本文详细介绍了一个基于强化学习(RL)的飞机升阻力特性预测模型的实现过程。首先,定义了飞机空气动力学环境,包括状态空间、动作空间以及目标——预测升力系数(Cl)和阻力系数(Cd)。接着,通过生成模拟数据并进行预处理,创建了用于训练的数据集。然后,构建了一个神经网络代理模型,用于联合编码状态和动作,并预测升阻力系数。最后,实现了PPO算法来训练强化学习代理,使其能够根据当前状态选择最优动作,并通过不断迭代提高预测精度。文中还提供了完整的代码实现和详细的注释。 适合人群:航空航天领域的研究人员、机器学习工程师、对强化学习感兴趣的开发者。 使用场景及目标:适用于需要预测飞机升阻力特性的应用场景,如飞行器设计优化、性能评估等。目标是通过强化学习方法提升预测模型的准确性,从而为实际工程提供可靠的理论支持和技术手段。 其他说明:本文不仅涵盖了模型的设计与实现,还包括了数据生成、预处理等多个环节,有助于读者全面理解整个建模过程。同时,提供的代码可以作为研究和开发的基础,方便进一步扩展和改进。
cmock ut aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
lsm6d datasheet
风力发电机传动机构的设计(增速器)
genesys-zu(5ev)配置petalinux(从安装到嵌入)
django自建博客app
Android项目原生java语言课程设计,包含LW+ppt
幼儿园预防肺结核教育培训课件资料
STM32F103RCT6单片机控制气泵和电磁阀的开关 1、气泵和电磁阀的开和关均为开关量,实现控制方法有多种,比如继电器,但是继电器动作有噪声且体积较大,更好的方法为使用mos管。 2、mos管的选型:mos管选择主要注意两个参数即可,一是导通的电流,二是耐压值,并且常用NMOS管,根据要求,气泵和电磁阀供电电压为12V,所以选择的mos管耐压值要大于12V,这里选用耐压值为30V的MOS管,并且导通电流为5.8A。
因文件较多,数据存放网盘,txt文件内包含下载链接及提取码,永久有效。失效会第一时间进行补充。样例数据及详细介绍参见文章:https://blog.csdn.net/T0620514/article/details/146916073
将 Windows 系统中 “C:\windows\fonts” 目录下的所有字体文件
智能量测终端最新标准