一、锁机制
SAP LUW要求数据库对象的锁定在SAP LUW结束释放,并且该数据库锁要求对所有SAP程序可见。SAP提供了一个逻辑数据锁定机制,该机制基于系统特定的锁定服务应用服务器中的中心锁定表(即将加锁的信息记入数据库表)。一个ABAP程序在访问数据之前,将希望锁定的数据表关键字发送给该表,因此所有的程序在访问一个数据库表之前必须首先判断该表是否已经被锁定了。
SAP锁定与数据库物理锁定是不同的,它是一种业务逻辑上的锁定。它不会在物理表上进行加锁,而是将关键字传递给加锁函数,加锁函数会在特定表中加锁信息登记。
SAP LUW在结束时(提交或回滚),SAP锁定将会隐式解除。
Lock objects are used to lock the database table while making the modifications on the database table. sap为了同步同时多个用户操作同一数据,防止数据出现不一致性而采用了锁机制。一般 sap会在操作数据前设置锁,防止第二个用户进行修改操作,当操作结束后系统会自动释放锁(当然程序也可以调用解锁函数去解锁)。
SAP为什么要设置锁:
1,保持数据的一致性
如果几个用户要访问同样的资源,需要找到一种同步访问的方法去保持数据的一致性。比如说,在航班预订系统中,需要检查还有没有空座位,当检查的时候,你不想别人修改重要的数据(空座位的数量)。
2,仅仅用Database锁是不够的
数据库管理系统物理锁定了要修改的行记录,其他用户要等到数据库锁释放才能访问这个记录。
在SAP系统中,当一个新屏幕显示的时候会释放掉Database锁,因为屏幕的改变会触发一个隐式的DB COMMIT。如果数据是从好几个屏幕收集来的话,而且在这段时间内这些数据会分别被锁定,仅仅用Database锁就不够了。
SAP系统在应用服务器层面有一个全局的LOCK TABLE,可以用来设置逻辑锁来锁定相关的表条目,并有ENQUEUE工作进程来管理这些锁。SAP锁是一种逻辑意义上的锁,有可能你锁定的表条目在DATABASE上根本就不存在。
二、加锁解锁的思路/步骤
程序设计思路:先上锁,上锁成功之后,从数据库取数据,然后更改数据,接着更新到数据库,最后解锁。按照这个步骤,才能保证更改完全运行在锁的保护机制下。
加锁维度:可以针对整个程序加锁,比如后台JOB程序,前一个没有执行完成,后一个不能启动。可以针对不同公司代码或不同的单据ID等加锁,根据不同业务场景需要确认锁的颗粒度。
三、建锁对象
在SE11里创建锁对象,自定义的锁对象都必须以EZ或者EY开头来命名。
新建完之后可以看到三个标签页。Attributes,Tables,Lock Parameter.
如果是接口对表进行操作,注意需要选上allow RFC.如图:
四、锁类型
独占锁:可写锁,一个用户正在修改数据时,阻止其他用户更改该数据。
共享锁:只读锁,一个用户正在读数据时,阻止其他用户更改该数据。
Exclusive lock: The locked data can only be displayed or edited by a single user.
A request for another exclusive lock or for a shared lock is rejected.
Shared lock: More than one user can access the locked data at the same time in display
mode. A request for another shared lock is accepted, even if it comes from another user.
An exclusive lock is rejected.
Exclusive but not cumulative: Exclusive locks can be requested several times from the
same transaction and are processed successively. In contrast, exclusive but not
cumulative locks can be called only once from the same transaction. All other lock
requests are rejected.
五、锁方式
Tables标签下Primary Tables下两个输入项,name是表的名字,
lock mode有三种模式,分别是S,E,X.含义如下:
S (Shared lock, read lock)共享锁
E (Exclusive lock, write lock)写入锁
X (eXclusive lock, extended write lock, cannot be cumulated)专用,不累积
一般情况下,使用E模式
括号内为同一程序(即同一事务内)内,括号外为非同一程序内
S共享:本身不需要更改数据,但是希望显示的数据不被别人更改。
E专用累积:当更改数据的时候设置为此模式。
X专用不累积:和E类似,但是不允许累加,完全独占。
1、如果你在一个程序里成功对一个锁对象加锁之后,如果模式为S,其他用户不能再对这个锁对象加E、X模式的锁,但是可以加S模式的锁;
2、如果你在一个程序里成功对一个锁对象加锁之后,如果模式为E,其他用户不能再对这个锁对象加E、X、S模式的任意一种锁;
3、如果你在一个程序里成功对一个锁对象加锁之后,如果模式为X,其他用户不能再对这个锁对象加E、X、S模式的任意一种锁;
4、如果你在一个程序里成功对一个锁对象加锁之后,如果模式为S,在这个程序,你还可以再对这个锁对象加S模式的锁,如果没有别的用户对其加S模式的锁,那么你还可以对其加E模式的锁,X模式的不可以;
5、如果你在一个程序里成功对一个锁对象加锁之后,如果模式为E,在这个程序,你还可以再对这个锁对象加E、S模式的锁,X模式的不可以;
6、如果你在一个程序里成功对一个锁对象加锁之后,如果模式为X,在这个程序,你不可以再对这个锁对象加E、X、S模式的锁。
LOCK parameters里面默认的参数时表的主键,这样因为可以唯一的确定表的一行,通常情况下不用修改。
一个锁对象里只包含一个PRIMARY TABLE,可以包含若干个SECONDARY TABLE,锁的模式有三种:E,S,X。LOCK PARAMETERS里填写你要根据哪些字段来锁定表条目。
模式E:当更改数据的时候设置为此模式。
模式S:本身不需要更改数据,但是希望显示的数据不被别人更改。
模式X:和E类似,但是不允许累加,完全独占。
如果你在一个程序里成功对一个锁对象加锁之后,如果模式为E,其他用户不能再对这个锁对象加E、X、S模式的任意一种锁;
如果你在一个程序里成功对一个锁对象加锁之后,如果模式为X,其他用户不能再对这个锁对象加E、X、S模式的任意一种锁;
如果你在一个程序里成功对一个锁对象加锁之后,如果模式为S,其他用户不能再对这个锁对象加E、X模式的锁,但是可以加S模式的锁;
如果你在一个程序里成功对一个锁对象加锁之后,如果模式为E,在这个程序,你还可以再对这个锁对象加E、S模式的锁,X模式的不可以。
如果你在一个程序里成功对一个锁对象加锁之后,如果模式为X,在这个程序,你不可以再对这个锁对象加E、X、S模式的锁。
如果你在一个程序里成功对一个锁对象加锁之后,如果模式为S,在这个程序,你还可以再对这个锁对象加S模式的锁,如果没有别的用户对其加S模式的锁,那么你还可以对其加E模式的锁。X模式的不可以。
当激活锁对象的时候,系统会自动创建两个FM,ENQUEUE_<锁对象名>和DEQUEUE_<锁对象名>,分别用来锁定和解锁。
六、加锁解锁
在SE11激活锁对象之后,系统会自动生成两个 FM 来进行锁管理,一个用来对对象进行锁定,另一个是释放对象。二者的名字都很有规律。
DEQUEUE_<lock object的名字> 释放lock object的锁定(lock the table)
ENQUEUE_<lock object的名字> 对象lock object要求的锁定(removing lock on the table)
到时要使用的时候直接call function module即可。
当用逻辑锁来锁定表条目的时候,系统会自动向LOCK TABLE中写入记录。
当调用设置锁的FM时,LOCK PARAMETERS如果没有指明,系统会锁定整个表。
当然,LOCK PARAMETER:CLIENT有点特殊,如果不指定,默认是SY-MANDT;
如果指定相应的CLIENT,会锁定对应CLIENT上的相应的表记录;如果设置为SPACE,则锁定涉及所有的CLIENT。
当逻辑锁设置失败后,一般会有两种例外。一个是EXCEPTION:FOREIGN_LOCK,意思是已经被锁定了;另一个是EXCEPTION:SYSTEM_FAILURE。
有些情况下,程序中设置成功的逻辑锁会隐式的自己解锁。比如说程序结束发生的时候(MESSAGE TYPE为A或者X的时候),使用语句LEAVE PROGRAM,LEAVE TO TRANSACTION,或者在命令行输入/n回车以后。
在程序的结束可以用DEQUEUE FUNCTION MODULE来解锁(当然如果你不写这个,程序结束的时候也会自动的解锁),这个时候,系统会自动从LOCK TABLE把相应的记录删除。使用DEQUEUE FUNCTION MODULE来解锁的时候,不会产生EXCEPTION。要解开你在程序中创建的所有的逻辑锁,可以用FM:DEQUEUE_ALL.
用于设锁的 FM 为: ENQUEUE_<锁对象名>。它用于在锁表(Lock Table)中生成一个锁项(Lock Entry)。若设锁不成功的话,就会在 Return 中反映出来。
用于释放锁的 FM 为:DEQUEUE_<锁对象名>。它用于从锁表中删除一个锁项。
在 ABAP 程序中,只需使用 "CALL FUNCITION ..." 语句就可以调用它们。
这两个锁 FM 是在 SAP 系统的一个特殊工作进程中执行的,专门进行锁管理。它运行在一个单独的服务器上,而该服务器专门用于维护整个 SAP 系统的主锁表(Central Lock Table)。
lock Table
CALL FUNCTION 'ENQUEUE_E_TABLE'
EXPORTING
tabname = table_name
EXCEPTIONS
foreign_lock = 1
system_failure = 2
OTHERS = 3.
Unlock Table
CALL FUNCTION 'DEQUEUE_E_TABLE'
EXPORTING
tabname = table_name
七、锁参数设置
锁参数SCOPE设置
_SCOPE = 1: The lock is not sent to the update program. The lock is removed when the transaction is ended.
_SCOPE = 2: The lock is sent to the update program. The update program is responsible for removing the lock. The dialog program which requested the lock no longer has an influence on the lock behavior. This is the standard setting for the ENQUEUE function module.
_SCOPE = 3: The lock
八、锁查看
如果程序出现死锁状态,可以通过TCODE: SM12去诊断,手工删除相关的锁定。
九、TCODE、相关表、多表锁
1)TCODE:SE11, SM12
可以通过se11来查看和创建锁对象,锁对象创建完毕之后,统会自动创建两个FM,ENQUEUE_<锁对象名>和DEQUEUE_<锁对象名>,分别用来锁定和解锁。
2)相关表:
DD25L:组合标题(方式,MC目标,锁定目标)(纪录了锁主表);
DD25T:
DD26S:视图的基本表和外来码关系(纪录了所有和锁相关的表);
DD27S:合计(视图,MC对象,锁定对象)字段;
3)多表锁
在SE11里创建锁对象,自定义的锁对象都必须以EZ或者EY开头来命名。一个锁对象里只包含一个PRIMARY TABLE,可以包含若干个SECONDARY TABLE。如多表锁:EMEKKOE,主表:EKKO-->E 专用累积,从表:EKPO-->E 专用累积。
十、锁的生命周期
通常程序一运行就要对特殊记录进行ENQUEUE处理,程序运行完毕之后进行DEQUEUE处理(不显示处理的话,关闭该session的时候,sap会自动DEQUEUE)。
十一、锁的作用范围
sap锁仅对特定程序起作用,假设A程序改table1的第1条记录,B程序也改table1的第1条记录,A程序一运行就对table1的第1条记录加锁,B程序则未加锁,则B程序仍可以改table1的第1条记录内容。
十二、通用的加锁和解锁函数
1、ENQUEUE_E_TABLE/DEQUEUE_E_TABLE,没创建锁对象的时候使用;
2、ENQUEUE_ESFUNCTION/DEQUEUE_ESFUNCTION,没创建锁对象的时候使用;
3、ENQUEUE_ES_PROG/DEQUEUE_ES_PROG,用于控制同一个程序不能并发执行;
4、DEQUEUE_ALL,解同一个程序中创建的所有锁。
十三、锁的应用
- 按表加锁
最常用的场景,按表的关键字等需要字段加锁解锁单据
2. 按程序加锁
Se11锁定义
锁对象名称 | EZ_ZSFI054 | 程序按公司锁定 |
属性 | 允许RFC勾选 | |
锁模式 | X专用,不累积 | |
锁主表 | ZSFI054结构 | |
锁参数 | ZSFI054结构所有字段 |
应用:一个程序在跑后台JOB没有执行完成,不可启用下一个job
FORM frm_check_job .
IF sy-batch EQ abap_true.
CALL FUNCTION 'ENQUEUE_EZ_ZSFI054'
EXPORTING
cprog = sy-cprog
cate = '1'
_scope = '1'
EXCEPTIONS
foreign_lock = 1
system_failure = 2
OTHERS = 3.
IF sy-subrc <> 0.
WRITE / '前一个后台任务还在运行中'.
STOP.
ENDIF.
ENDIF.
ENDFORM.
- 对编号范围对象SNRO加锁
应用:ZFI* 收入共享平台任务推送程序
FORM frm_get_next_id USING uv_bukrs CHANGING cv_id.
DATA: lv_id TYPE char6,
quant LIKE inri-quantity, "dummy
code LIKE inri-returncode. "returncode
CALL FUNCTION 'NUMBER_RANGE_ENQUEUE'
EXPORTING
object = 'ZFI0047'
EXCEPTIONS
foreign_lock = 1
object_not_found = 2
system_failure = 3
OTHERS = 4.
IF sy-subrc = 0.
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = '01'
object = 'ZFI0047'
IMPORTING
number = lv_id
quantity = quant
returncode = code
EXCEPTIONS
interval_not_found = 1
number_range_not_intern = 2
object_not_found = 3
quantity_is_0 = 4
quantity_is_not_1 = 5
interval_overflow = 6
buffer_overflow = 7
OTHERS = 8.
CALL FUNCTION 'NUMBER_RANGE_DEQUEUE'
EXPORTING
object = 'ZFI0047'
EXCEPTIONS
object_not_found = 1
OTHERS = 2.
CONCATENATE uv_bukrs 'TK' sy-datum+0(6) lv_id INTO cv_id.
ENDIF.
ENDFORM.
check this link :
SAP Help Portal
http://help.sap.com/saphelp_40b/helpdata/en/cf/21eea5446011d189700000e8322d00/content.htm