为了提高性能,SAP ERP提供了多种编号范围缓冲机制,常用的有两种,主内存缓冲和并行缓冲,他们的特征分别如下:
主内存缓冲(Main Memory Buffering)
工作原理:
主内存缓冲将编号范围缓存在每个应用服务器的内存中。
当需要新的编号时,系统从内存中获取,而不是每次都访问数据库。
优点:
提高了性能,因为减少了对数据库的访问频率。
对于高频率的编号请求,响应速度更快。
缺点:
无法保证编号的连续性和无间隙性,因为在系统崩溃或重启时,内存中的编号可能会丢失。
在多服务器环境中,不同服务器之间的编号分配可能不一致,导致编号顺序混乱。
并行缓冲(Parallel Buffering)
工作原理:
并行缓冲使用一个透明表(NRIVSHADOW)来管理编号范围。
每个应用服务器和工作进程都有自己的缓冲区,从缓冲表中获取编号。
在回滚的情况下,编号会重新回到缓冲表中,可以在下次请求时再次使用。
优点:
解决了本地缓冲在并行批处理中的问题,提供了更好的并行处理能力。
编号分配尽可能接近未缓冲的行为,基本上是按时间顺序递增的。
在回滚的情况下,编号不会丢失,而是重新回到缓冲表中,确保编号的连续性和无间隙性。
缺点:
实现相对复杂,需要额外的表来管理编号。
在极少数情况下,编号顺序可能会因为回滚而暂时不按时间顺序递增。
总结
主内存缓冲适用于对性能要求较高且不严格要求编号连续性的场景,但在多服务器环境中可能会导致编号顺序混乱。
并行缓冲适用于需要高并发处理且要求编号尽可能连续和按时间顺序递增的场景,尽管实现相对复杂,但提供了更好的编号管理和一致性。
工具程序:
如果是主内存缓冲,在做Client Copy后,或者缓冲条数即将超过设置的条数,就需要重置缓冲,这个工作可以在ST02和SM56来完成,但是这个时候如果有一个工具来查看和重置,会更方便一些。
下面是这个工具的运行界面:
程序去掉了SCREEN和Status,方便移植。代码如下:
*&---------------------------------------------------------------------*
*& 号码范围缓冲区查看/重置
*&---------------------------------------------------------------------*
*& Baitianzhen
*&---------------------------------------------------------------------*
REPORT zsnrobuff NO STANDARD PAGE HEADING.
TABLES: noselect.
DATA: gt_fldct TYPE lvc_t_fcat,
gs_slayt TYPE lvc_s_layo,
gt_event TYPE lvc_t_evts,
gs_event TYPE lvc_s_evts,
gv_repid TYPE sy-repid.
DATA: gr_grid TYPE REF TO cl_gui_alv_grid.
DATA: BEGIN OF gs_memo.
INCLUDE TYPE bnriv2.
DATA: iname TYPE msxxlist_v6-name,
seled TYPE c,
END OF gs_memo.
DATA: gt_memo LIKE TABLE OF gs_memo.
DATA: BEGIN OF gs_para.
INCLUDE TYPE nrivshadow.
DATA: seled TYPE c,
END OF gs_para.
DATA: gt_para LIKE TABLE OF gs_para.
DATA: gt_server TYPE TABLE OF msxxlist_v6 WITH HEADER LINE.
DATA: gv_rtmsg TYPE bapi_msg.
CLASS lcl_event_receiver DEFINITION DEFERRED .
DATA: event_receiver TYPE REF TO lcl_event_receiver.
*&---------------------------------------------------------------------*
*& lcl_event_receiver
*&---------------------------------------------------------------------*
CLASS lcl_event_receiver DEFINITION.
PUBLIC SECTION.
METHODS handle_user_command
FOR EVENT user_command OF cl_gui_alv_grid
IMPORTING e_ucomm.
METHODS handle_toolbar
FOR EVENT toolbar OF cl_gui_alv_grid
IMPORTING e_object e_interactive.
ENDCLASS. "lcl_event_receiver9001 DEFINITION
*&---------------------------------------------------------------------*
*& lcl_event_receiver
*&---------------------------------------------------------------------*
CLASS lcl_event_receiver IMPLEMENTATION.
METHOD handle_user_command.
CASE e_ucomm.
WHEN 'RESET'.
PERFORM reset USING ''.
PERFORM refresh.
WHEN 'REFRESH'.
PERFORM refresh.
ENDCASE.
ENDMETHOD. "handle_user_command
METHOD handle_toolbar.
PERFORM add_button USING e_object
: '3' '' '' '',
' ' 'REFRESH' icon_refresh ' 刷新',
'3' '' '' '',
' ' 'RESET ' '' '重置'.
ENDMETHOD. "handle_toolbar
ENDCLASS. "lcl_event_receiver9001 IMPLEMENTATION
SELECTION-SCREEN BEGIN OF BLOCK b0 WITH FRAME TITLE btxt0.
PARAMETERS: pr_memo RADIOBUTTON GROUP typ USER-COMMAND sele DEFAULT 'X'.
PARAMETERS: pr_para RADIOBUTTON GROUP typ.
SELECTION-SCREEN END OF BLOCK b0.
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE btxt1.
SELECT-OPTIONS: s_server FOR gt_server-name NO INTERVALS OBLIGATORY,
s_object FOR gs_memo-object.
SELECTION-SCREEN END OF BLOCK b1.
AT SELECTION-SCREEN OUTPUT.
btxt0 = '缓冲类型'.
btxt1 = '数据选择'.
%_pr_memo_%_app_%-text = '主内存缓冲'.
%_pr_para_%_app_%-text = '并行缓冲'.
%_s_server_%_app_%-text = '服务器'.
%_s_object_%_app_%-text = '编号范围对象'.
INITIALIZATION.
CALL FUNCTION 'TH_SERVER_LIST'
TABLES
list = gt_server
EXCEPTIONS
no_server_list = 1
OTHERS = 2.
LOOP AT gt_server.
s_server-sign = 'I'.
s_server-option = 'EQ'.
s_server-low = gt_server-name.
COLLECT s_server.
ENDLOOP.
START-OF-SELECTION.
PERFORM savelog IN PROGRAM zreplog USING sy-repid '' IF FOUND.
PERFORM getdata.
PERFORM updatelog IN PROGRAM zreplog IF FOUND.
IF pr_memo = 'X'.
PERFORM outdata TABLES gt_memo.
ELSE.
PERFORM outdata TABLES gt_para.
ENDIF.
*&---------------------------------------------------------------------*
*& 获取数据,程序执行和刷新的时候调用
*&---------------------------------------------------------------------*
FORM getdata.
DATA: hitkz.
DATA: lt_data TYPE TABLE OF bnriv2 WITH HEADER LINE.
CLEAR: gt_memo,gs_memo,gs_para,gt_para.
CASE 'X'.
WHEN pr_memo.
LOOP AT s_server.
CLEAR lt_data[].
CALL FUNCTION 'SAPTUNE_NUMBUF_CONTENTS' DESTINATION s_server-low
TABLES
contents = lt_data
EXCEPTIONS
system_failure = 1 MESSAGE gv_rtmsg
communication_failure = 2 MESSAGE gv_rtmsg
OTHERS = 4.
IF sy-subrc = 0.
LOOP AT lt_data WHERE object IN s_object.
MOVE-CORRESPONDING lt_data TO gs_memo.
gs_memo-iname = s_server-low.
APPEND gs_memo TO gt_memo.
ENDLOOP.
ELSE.
MESSAGE i000(oo) WITH '服务器Error:' s_server-low gv_rtmsg.
ENDIF.
ENDLOOP.
WHEN pr_para.
SELECT * INTO CORRESPONDING FIELDS OF TABLE gt_para
FROM nrivshadow CLIENT SPECIFIED
WHERE object IN s_object.
LOOP AT gt_para INTO gs_para.
CLEAR hitkz.
LOOP AT s_server.
IF gs_para-instanz CS s_server-low.
hitkz = 'X'.
EXIT.
ENDIF.
ENDLOOP.
IF hitkz IS INITIAL.
DELETE gt_para.
CONTINUE.
ENDIF.
ENDLOOP.
ENDCASE.
ENDFORM. "getdata
*&---------------------------------------------------------------------*
*& outdata
*&---------------------------------------------------------------------*
FORM outdata TABLES t_out.
gv_repid = sy-repid.
gs_slayt-zebra = 'X'.
gs_slayt-detailinit = 'X'.
gs_slayt-box_fname = 'SELED'.
gs_event-name = slis_ev_caller_exit_at_start.
gs_event-form = 'REG_EVENT'.
APPEND gs_event TO gt_event.
IF pr_memo = 'X'.
PERFORM catset1 TABLES gt_fldct
USING: 'INAME ' ' ' ' ' '实例',
'CLIENT ' 'BNRIV2' 'CLIENT ' 'CLIENT',
'OBJECT ' 'BNRIV2' 'OBJECT ' '',
'SUBOBJECT ' 'BNRIV2' 'SUBOBJECT ' '',
'NRRANGENR ' 'BNRIV2' 'NRRANGENR ' '',
'TOYEAR ' 'BNRIV2' 'TOYEAR ' '',
'FROMNUMBER' 'BNRIV2' 'FROMNUMBER' '',
'TONUMBER ' 'BNRIV2' 'TONUMBER ' '',
'NRLEVEL ' 'BNRIV2' 'NRLEVEL ' '',
'EXTERNIND ' 'BNRIV2' 'EXTERNIND ' ''.
ELSE.
PERFORM catset1 TABLES gt_fldct
USING: 'INSTANZ ' 'NRIVSHADOW' 'INSTANZ ' '实例和进程标识',
'CLIENT ' 'NRIVSHADOW' 'CLIENT ' 'CLIENT',
'OBJECT ' 'NRIVSHADOW' 'OBJECT ' '对象名称',
'SUBOBJECT' 'NRIVSHADOW' 'SUBOBJECT' '子对象',
'NRRANGENR' 'NRIVSHADOW' 'NRRANGENR' '序号区间编号',
'TOYEAR ' 'NRIVSHADOW' 'TOYEAR ' '终止年',
'FROMNR ' 'NRIVSHADOW' 'FROMNR ' '缓冲区自',
'TONUMBER ' 'NRIVSHADOW' 'TONUMBER ' '缓冲区至',
'NRLEVEL ' 'NRIVSHADOW' 'NRLEVEL ' '编号范围状态',
'G_FROMNR ' 'NRIVSHADOW' 'G_FROMNR ' '间隔自',
'G_TONR ' 'NRIVSHADOW' 'G_TONR ' '间隔至'.
ENDIF.
CHECK t_out[] IS NOT INITIAL.
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'
EXPORTING
it_fieldcat_lvc = gt_fldct
it_events = gt_event
is_layout_lvc = gs_slayt
i_callback_program = gv_repid
i_callback_pf_status_set = 'SET_STATUS'
TABLES
t_outtab = t_out.
ENDFORM. " outdata
*&---------------------------------------------------------------------*
*& set_status
*&---------------------------------------------------------------------*
FORM set_status USING pt_extab TYPE slis_t_extab.
SET PF-STATUS 'STLI' OF PROGRAM 'SAPMSSY0'.
ENDFORM. "set_status
*&---------------------------------------------------------------------*
*& reg_event
*&---------------------------------------------------------------------*
FORM reg_event USING po_grid TYPE slis_data_caller_exit.
DATA: lt_func TYPE TABLE OF ui_func WITH HEADER LINE.
FIELD-SYMBOLS: <fs> TYPE any.
CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR'
IMPORTING
e_grid = gr_grid.
ASSIGN ('(SAPLSLVC_FULLSCREEN)GT_GRID-S_LVC_LAYOUT-NO_TOOLBAR') TO <fs>.
IF <fs> IS ASSIGNED.
CLEAR <fs>.
ENDIF.
APPEND cl_gui_alv_grid=>mc_fc_info TO lt_func.
APPEND cl_gui_alv_grid=>mc_mb_subtot TO lt_func.
APPEND cl_gui_alv_grid=>mc_mb_sum TO lt_func.
ASSIGN ('(SAPLSLVC_FULLSCREEN)GT_GRID-T_EXCLUDING_LVC') TO <fs>.
IF <fs> IS ASSIGNED.
<fs> = lt_func[].
ENDIF.
CREATE OBJECT event_receiver.
SET HANDLER event_receiver->handle_user_command FOR gr_grid.
SET HANDLER event_receiver->handle_toolbar FOR gr_grid.
ENDFORM. "reg_event
*&---------------------------------------------------------------------*
*& Fieldcat
*&---------------------------------------------------------------------*
FORM catset1 TABLES t_fldcat USING pv_field pv_reftab pv_reffld pv_text.
DATA: ls_fldcat TYPE lvc_s_fcat.
ls_fldcat-fieldname = pv_field.
ls_fldcat-reptext = pv_text.
ls_fldcat-coltext = pv_text.
ls_fldcat-colddictxt = 'R'.
ls_fldcat-selddictxt = 'R'.
ls_fldcat-ref_table = pv_reftab.
ls_fldcat-ref_field = pv_reffld.
ls_fldcat-col_opt = 'A'.
APPEND ls_fldcat TO t_fldcat.
CLEAR ls_fldcat.
ENDFORM. "catset1
*&---------------------------------------------------------------------*
*& 重置缓冲区
*&---------------------------------------------------------------------*
FORM reset USING global TYPE noselect-norsetglob.
DATA: opcode TYPE x VALUE 3,
rsmode TYPE x.
IF pr_para = 'X'.
MESSAGE s000(oo) WITH '并行缓冲请使用程序NK_REORGANIZE重置'.
RETURN.
ENDIF.
IF global = ''.
rsmode = 1.
ELSE.
rsmode = 2.
ENDIF.
LOOP AT gt_memo INTO gs_memo WHERE seled = 'X'.
noselect-client = gs_memo-client.
noselect-object = gs_memo-object.
noselect-subobject = gs_memo-subobject.
noselect-nrrangenr = gs_memo-nrrangenr.
noselect-toyear = gs_memo-toyear.
noselect-norsetglob = global.
CALL 'ThNoCall' ID 'OPCODE' FIELD opcode
ID 'BNRIV' FIELD noselect
ID 'RESETMODE' FIELD rsmode.
ENDLOOP.
IF sy-subrc NE 0.
MESSAGE s000(oo) WITH '请选中需要重置的行'.
ENDIF.
ENDFORM. "reset
*&---------------------------------------------------------------------*
*& ALV自带工具栏添加按钮
*&---------------------------------------------------------------------*
FORM add_button USING pr_object TYPE REF TO cl_alv_event_toolbar_set
pv_type pv_func pv_icon pv_text.
DATA: ls_toolbar TYPE stb_button.
CLEAR ls_toolbar.
ls_toolbar-butn_type = pv_type.
ls_toolbar-function = pv_func.
ls_toolbar-icon = pv_icon.
ls_toolbar-text = pv_text.
APPEND ls_toolbar TO pr_object->mt_toolbar.
ENDFORM. "add_button
*&---------------------------------------------------------------------*
*& 刷新数据
*&---------------------------------------------------------------------*
FORM refresh.
DATA ls_stable TYPE lvc_s_stbl.
ls_stable-row = 'X'.
ls_stable-col = 'X'.
PERFORM getdata.
CALL METHOD gr_grid->refresh_table_display
EXPORTING
is_stable = ls_stable
EXCEPTIONS
finished = 1
OTHERS = 2.
ENDFORM. "refresh