BIND_MISMATCH导致过多VERSION COUNT的问题

本文解析了 Oracle 数据库中的 Bind Graduation 机制,详细介绍了绑定变量如何影响游标的共享及硬解析次数,并探讨了解决 BIND_MISMATCH 问题的方法。

并不是用了绑定变量就一定都会游标共享,下面我们介绍的就是一种例子。BIND_MISMATCH导致VERSION COUNT过多的原因解释:

This is due to the bind buffer mismatch of the current child cursor. If oracle is unable to bind the current value to the existing child cursors bind buffer, 
Oracle upgrades the existing child cursor with a high bind buffer. This will force the query to do a hard parse and a new child cursor will be created.

对于绑定变量,ORACLE根据变量长度进行了分级,对于VARCHAR2类型共有如下4级:

第一级: 1-32
第二级: 33-128
第三级: 129-2000
第四级: 2000+

Oracle在进行bind graduation的时候,使用的是绑定变量的声明类型长度。对于定义的变量在同一级可以共享游标,否则会生成子游标,如下:

SQL> desc t
 名称                                      是否为空? 类型
 ----------------------------------------- -------- ----------------------------
 X                                                  VARCHAR2(30)

SQL> variable v_x varchar2(32)

SQL> exec :v_x:='a';


PL/SQL 过程已成功完成。

SQL> select * from t where x=:v_x;

未选定行

SQL>  select sql_id,child_number,executions from v$sql where sql_text='select * from t where x=:v_x';

SQL_ID        CHILD_NUMBER EXECUTIONS
------------- ------------ ----------
1pqg8dpwthcp3            1          1

SQL> variable v_x varchar2(33)
SQL> exec :v_x:='a';

PL/SQL 过程已成功完成。

SQL> select * from t where x=:v_x;

未选定行

SQL> select sql_id,child_number,executions from v$sql where sql_text='select * from t where x=:v_x';

SQL_ID        CHILD_NUMBER EXECUTIONS
------------- ------------ ----------
1pqg8dpwthcp3            1          1
1pqg8dpwthcp3            2          1

SQL> select  child_number,bind_mismatch from v$sql_shared_cursor where sql_id='1pqg8dpwthcp3';

CHILD_NUMBER B
------------ -
           1 N
           2 Y

SQL> variable v_x varchar2(129)
SQL> exec :v_x:='a';

PL/SQL 过程已成功完成。

SQL> select * from t where x=:v_x;

未选定行

SQL> select sql_id,child_number,executions from v$sql where sql_text='select * from t where x=:v_x';

SQL_ID        CHILD_NUMBER EXECUTIONS
------------- ------------ ----------
1pqg8dpwthcp3            1           1
1pqg8dpwthcp3            2          1
1pqg8dpwthcp3            3          1

SQL>  select  child_number,bind_mismatch from v$sql_shared_cursor where sql_id='1pqg8dpwthcp3';

CHILD_NUMBER B
------------ -
           1 N
           2 Y
           3 Y


SQL> variable v_x varchar2(2001)
SQL> exec :v_x:='a';

PL/SQL 过程已成功完成。

SQL> select * from t where x=:v_x;

未选定行

SQL> select sql_id,child_number,executions from v$sql where sql_text='select * from t where x=:v_x';

SQL_ID        CHILD_NUMBER EXECUTIONS
------------- ------------ ----------
1pqg8dpwthcp3            1          1
1pqg8dpwthcp3            2          1
1pqg8dpwthcp3            3          1
1pqg8dpwthcp3            4          1

SQL>  select  child_number,bind_mismatch from v$sql_shared_cursor where sql_id='1pqg8dpwthcp3';

CHILD_NUMBER B
------------ -
           1 N
           2 Y
           3 Y
           4 Y
具体可以参考:High Version Count Due To BIND_MISMATCH [ID 336268.1]    

ORACLE文档说可以通过设置10503事件来搞定这个问题 , 10503 => enable user-specified graduated bind lengths


(1)查询绑定变量最大长度:假如最大长度为128
select max(max_length) from v$sql_bind_metadata where datatype=1;


(2)在参数文件中设置:
event="10503 trace name context forever, level 128"

SQL> alter system set event='10503 trace name context forever, level 128' scope=spfile;


(3)该参数在会话级别虽然能够成功设置,但是无效。
Bug 10274265 - Event 10503 does not work at session level [ID 10274265.8]
SQL> select * from v$version;BANNER

----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE    10.2.0.1.0      Production
TNS for 32-bit Windows: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production   

SQL> alter system flush shared_pool;
系统已更改。


SQL> alter system set events '10503  trace name context forever ,level 4096';
系统已更改。

SQL> variable c varchar2(32);
SQL> exec :c := 'zheng';
SQL> select id from edu.zhhtest where name= :c and rownum=1;

SQL> variable c varchar2(33);
SQL> exec :c := 'zheng';
SQL> select id from edu.zhhtest where name= :c and rownum=1;


SQL> select sql_id,child_number,executions from v$sql where sql_text='select id from edu.zhhtest where name= :c and rownum=1';

SQL_ID        CHILD_NUMBER EXECUTIONS
------------- ------------ ----------
grzn6d2ak22j4            0          2

SQL> select s.child_number, m.position, m.max_length,
         decode(m.datatype,1,'varchar2',2,'number',m.datatype) as datatype
  3    from v$sql s, v$sql_bind_metadata m
  4    where s.sql_id = 'grzn6d2ak22j4'
  5    and s.child_address = m.address
  6    order by 1, 2;

CHILD_NUMBER   POSITION MAX_LENGTH DATATYPE
------------ ---------- ---------- ----------------------------------------
           0          1       4000 varchar2

 

 

Bind Graduation的目的是什么?可能原因有如下两个:

 

  1. bind peeking缓解,提供多次peeking机会

 

从效果来看,Oracle bind graduation会增加子游标的数量。如果单就bind peeking而言,在Oracle 11g的ACS(Adaptive Cursor Sharing)出现之前,

Oracle绑定变量使用的子游标数量是很少的。Bind graduation出现之后,我们最直观的感觉是child cursor增多,对应的执行计划增多。

原有的可能只用一个执行计划可以覆盖的绑定变量语句,可能要有多个执行计划才能覆盖。

 

对绑定变量语句而言,每次生成子游标,就意味着要进行一次hard parse,就意味着要进行一次peeking。生成与Peeking value对应的执行计划。

代码中对变量声明长度的不一致,直接意味着不同的程序模块和功能模块。Oracle也许认为这样出现bind peeking问题的几率较高。于是取巧采用变量声明的方式进行区分管理。

同时,划分区域又不是很多,从而限制了子游标出现的数量。多次peeking,形成多个子游标,配对更合适的执行计划。

 

 2.   绑定变量存储

 

对执行计划而言,Oracle是需要单独分配内存空间给执行计划进行保存的。如果其中有使用绑定变量,Oracle是会将绑定变量保存在child cursor中的。

在分配varchar2类型的绑定变量大小空间时,使用bind graduation可以分配略小的适当空间。

虽然会存在bind graduation现象,但是我们说实现graduation的分区数量是有限的。也就是说,即使多次生成child cursor,带来version count过多的风险也是有限的。

如果要是很极端的情况,比如项目组希望实现绝对的共享或者说变量数目较多引起version count过多,可以使用10503事件控制bind graduation的出现,或者直接在代码中声明

varchar2(2000)的绑定变量即可。

 相关的视图:

  •  v$sql_bind_capture;
  •  v$sql_bind_metadata 
  •  v$sql_shared_cursor 
  •  v$sql 
  •  v$sqlarea

select s.child_number, m.position, m.max_length,decode(m.datatype,1,'varchar2',2,'number',m.datatype) as datatype
from v$sql s, v$sql_bind_metadata m where s.sql_id = 'abz9zj4ryuw67' and s.child_address = m.address order by 1, 2;

select sql_id,child_number,bind_mismatch from v$sql_shared_cursor where sql_id='abz9zj4ryuw67';

11GR2的测试结果是第一次会是BIND_LENGTH_UPGRADEABLE,然后才会出现BIND_MISMATCH,估计是单纯的增加了绑定变量buffer的长度,从而生产一个新的计划.

而不是重新bind peeking 然后生产计划。当对于一个SQL_ID有过一次bind_length_upgrade后,如果再次因为绑定变量长度不一样,不能重用计划是,就会bind peeking产生新的计划。

参考链接

http://blog.itpub.net/17203031/viewspace-704144

http://blog.itpub.net/17203031/viewspace-704348

http://blog.itpub.net/758322/viewspace-750315/

http://www.net527.cn/shujukuguanli/Oracle/2012/0407/22446.html

解释代码:// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.slf4j; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; import org.slf4j.event.SubstituteLoggingEvent; import org.slf4j.helpers.NOP_FallbackServiceProvider; import org.slf4j.helpers.Reporter; import org.slf4j.helpers.SubstituteLogger; import org.slf4j.helpers.SubstituteServiceProvider; import org.slf4j.helpers.Util; import org.slf4j.spi.SLF4JServiceProvider; public final class LoggerFactory { static final String CODES_PREFIX = "https://www.slf4j.org/codes.html"; static final String NO_PROVIDERS_URL = "https://www.slf4j.org/codes.html#noProviders"; static final String IGNORED_BINDINGS_URL = "https://www.slf4j.org/codes.html#ignoredBindings"; static final String MULTIPLE_BINDINGS_URL = "https://www.slf4j.org/codes.html#multiple_bindings"; static final String VERSION_MISMATCH = "https://www.slf4j.org/codes.html#version_mismatch"; static final String SUBSTITUTE_LOGGER_URL = "https://www.slf4j.org/codes.html#substituteLogger"; static final String LOGGER_NAME_MISMATCH_URL = "https://www.slf4j.org/codes.html#loggerNameMismatch"; static final String REPLAY_URL = "https://www.slf4j.org/codes.html#replay"; static final String UNSUCCESSFUL_INIT_URL = "https://www.slf4j.org/codes.html#unsuccessfulInit"; static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory in failed state. Original exception was thrown EARLIER. See also https://www.slf4j.org/codes.html#unsuccessfulInit"; public static final String PROVIDER_PROPERTY_KEY = "slf4j.provider"; static final int UNINITIALIZED = 0; static final int ONGOING_INITIALIZATION = 1; static final int FAILED_INITIALIZATION = 2; static final int SUCCESSFUL_INITIALIZATION = 3; static final int NOP_FALLBACK_INITIALIZATION = 4; static volatile int INITIALIZATION_STATE = 0; static final SubstituteServiceProvider SUBST_PROVIDER = new SubstituteServiceProvider(); static final NOP_FallbackServiceProvider NOP_FALLBACK_SERVICE_PROVIDER = new NOP_FallbackServiceProvider(); static final String DETECT_LOGGER_NAME_MISMATCH_PROPERTY = "slf4j.detectLoggerNameMismatch"; static final String JAVA_VENDOR_PROPERTY = "java.vendor.url"; static boolean DETECT_LOGGER_NAME_MISMATCH = Util.safeGetBooleanSystemProperty("slf4j.detectLoggerNameMismatch"); static volatile SLF4JServiceProvider PROVIDER; private static final String[] API_COMPATIBILITY_LIST = new String[]{"2.0"}; private static final String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class"; static List<SLF4JServiceProvider> findServiceProviders() { List<SLF4JServiceProvider> providerList = new ArrayList(); ClassLoader classLoaderOfLoggerFactory = LoggerFactory.class.getClassLoader(); SLF4JServiceProvider explicitProvider = loadExplicitlySpecified(classLoaderOfLoggerFactory); if (explicitProvider != null) { providerList.add(explicitProvider); return providerList; } else { ServiceLoader<SLF4JServiceProvider> serviceLoader = getServiceLoader(classLoaderOfLoggerFactory); Iterator<SLF4JServiceProvider> iterator = serviceLoader.iterator(); while(iterator.hasNext()) { safelyInstantiate(providerList, iterator); } return providerList; } } private static ServiceLoader<SLF4JServiceProvider> getServiceLoader(ClassLoader classLoaderOfLoggerFactory) { SecurityManager securityManager = System.getSecurityManager(); ServiceLoader serviceLoader; if (securityManager == null) { serviceLoader = ServiceLoader.load(SLF4JServiceProvider.class, classLoaderOfLoggerFactory); } else { PrivilegedAction<ServiceLoader<SLF4JServiceProvider>> action = () -> { return ServiceLoader.load(SLF4JServiceProvider.class, classLoaderOfLoggerFactory); }; serviceLoader = (ServiceLoader)AccessController.doPrivileged(action); } return serviceLoader; } private static void safelyInstantiate(List<SLF4JServiceProvider> providerList, Iterator<SLF4JServiceProvider> iterator) { try { SLF4JServiceProvider provider = (SLF4JServiceProvider)iterator.next(); providerList.add(provider); } catch (ServiceConfigurationError var3) { ServiceConfigurationError e = var3; Reporter.error("A service provider failed to instantiate:\n" + e.getMessage()); } } private LoggerFactory() { } static void reset() { INITIALIZATION_STATE = 0; } private static final void performInitialization() { bind(); if (INITIALIZATION_STATE == 3) { versionSanityCheck(); } } private static final void bind() { try { List<SLF4JServiceProvider> providersList = findServiceProviders(); reportMultipleBindingAmbiguity(providersList); if (providersList != null && !providersList.isEmpty()) { PROVIDER = (SLF4JServiceProvider)providersList.get(0); PROVIDER.initialize(); INITIALIZATION_STATE = 3; reportActualBinding(providersList); } else { INITIALIZATION_STATE = 4; Reporter.warn("No SLF4J providers were found."); Reporter.warn("Defaulting to no-operation (NOP) logger implementation"); Reporter.warn("See https://www.slf4j.org/codes.html#noProviders for further details."); Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); reportIgnoredStaticLoggerBinders(staticLoggerBinderPathSet); } postBindCleanUp(); } catch (Exception var2) { Exception e = var2; failedBinding(e); throw new IllegalStateException("Unexpected initialization failure", e); } } static SLF4JServiceProvider loadExplicitlySpecified(ClassLoader classLoader) { String explicitlySpecified = System.getProperty("slf4j.provider"); if (null != explicitlySpecified && !explicitlySpecified.isEmpty()) { String message; try { String message = String.format("Attempting to load provider \"%s\" specified via \"%s\" system property", explicitlySpecified, "slf4j.provider"); Reporter.info(message); Class<?> clazz = classLoader.loadClass(explicitlySpecified); Constructor<?> constructor = clazz.getConstructor(); Object provider = constructor.newInstance(); return (SLF4JServiceProvider)provider; } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException | ClassNotFoundException var6) { ReflectiveOperationException e = var6; message = String.format("Failed to instantiate the specified SLF4JServiceProvider (%s)", explicitlySpecified); Reporter.error(message, e); return null; } catch (ClassCastException var7) { ClassCastException e = var7; message = String.format("Specified SLF4JServiceProvider (%s) does not implement SLF4JServiceProvider interface", explicitlySpecified); Reporter.error(message, e); return null; } } else { return null; } } private static void reportIgnoredStaticLoggerBinders(Set<URL> staticLoggerBinderPathSet) { if (!staticLoggerBinderPathSet.isEmpty()) { Reporter.warn("Class path contains SLF4J bindings targeting slf4j-api versions 1.7.x or earlier."); Iterator var1 = staticLoggerBinderPathSet.iterator(); while(var1.hasNext()) { URL path = (URL)var1.next(); Reporter.warn("Ignoring binding found at [" + path + "]"); } Reporter.warn("See https://www.slf4j.org/codes.html#ignoredBindings for an explanation."); } } static Set<URL> findPossibleStaticLoggerBinderPathSet() { Set<URL> staticLoggerBinderPathSet = new LinkedHashSet(); try { ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader(); Enumeration paths; if (loggerFactoryClassLoader == null) { paths = ClassLoader.getSystemResources("org/slf4j/impl/StaticLoggerBinder.class"); } else { paths = loggerFactoryClassLoader.getResources("org/slf4j/impl/StaticLoggerBinder.class"); } while(paths.hasMoreElements()) { URL path = (URL)paths.nextElement(); staticLoggerBinderPathSet.add(path); } } catch (IOException var4) { IOException ioe = var4; Reporter.error("Error getting resources from path", ioe); } return staticLoggerBinderPathSet; } private static void postBindCleanUp() { fixSubstituteLoggers(); replayEvents(); SUBST_PROVIDER.getSubstituteLoggerFactory().clear(); } private static void fixSubstituteLoggers() { synchronized(SUBST_PROVIDER) { SUBST_PROVIDER.getSubstituteLoggerFactory().postInitialization(); Iterator var1 = SUBST_PROVIDER.getSubstituteLoggerFactory().getLoggers().iterator(); while(var1.hasNext()) { SubstituteLogger substLogger = (SubstituteLogger)var1.next(); Logger logger = getLogger(substLogger.getName()); substLogger.setDelegate(logger); } } } static void failedBinding(Throwable t) { INITIALIZATION_STATE = 2; Reporter.error("Failed to instantiate SLF4J LoggerFactory", t); } private static void replayEvents() { LinkedBlockingQueue<SubstituteLoggingEvent> queue = SUBST_PROVIDER.getSubstituteLoggerFactory().getEventQueue(); int queueSize = queue.size(); int count = 0; int maxDrain = true; List<SubstituteLoggingEvent> eventList = new ArrayList(128); while(true) { int numDrained = queue.drainTo(eventList, 128); if (numDrained == 0) { return; } Iterator var6 = eventList.iterator(); while(var6.hasNext()) { SubstituteLoggingEvent event = (SubstituteLoggingEvent)var6.next(); replaySingleEvent(event); if (count++ == 0) { emitReplayOrSubstituionWarning(event, queueSize); } } eventList.clear(); } } private static void emitReplayOrSubstituionWarning(SubstituteLoggingEvent event, int queueSize) { if (event.getLogger().isDelegateEventAware()) { emitReplayWarning(queueSize); } else if (!event.getLogger().isDelegateNOP()) { emitSubstitutionWarning(); } } private static void replaySingleEvent(SubstituteLoggingEvent event) { if (event != null) { SubstituteLogger substLogger = event.getLogger(); String loggerName = substLogger.getName(); if (substLogger.isDelegateNull()) { throw new IllegalStateException("Delegate logger cannot be null at this state."); } else { if (!substLogger.isDelegateNOP()) { if (substLogger.isDelegateEventAware()) { if (substLogger.isEnabledForLevel(event.getLevel())) { substLogger.log(event); } } else { Reporter.warn(loggerName); } } } } } private static void emitSubstitutionWarning() { Reporter.warn("The following set of substitute loggers may have been accessed"); Reporter.warn("during the initialization phase. Logging calls during this"); Reporter.warn("phase were not honored. However, subsequent logging calls to these"); Reporter.warn("loggers will work as normally expected."); Reporter.warn("See also https://www.slf4j.org/codes.html#substituteLogger"); } private static void emitReplayWarning(int eventCount) { Reporter.warn("A number (" + eventCount + ") of logging calls during the initialization phase have been intercepted and are"); Reporter.warn("now being replayed. These are subject to the filtering rules of the underlying logging system."); Reporter.warn("See also https://www.slf4j.org/codes.html#replay"); } private static final void versionSanityCheck() { try { String requested = PROVIDER.getRequestedApiVersion(); boolean match = false; String[] var2 = API_COMPATIBILITY_LIST; int var3 = var2.length; for(int var4 = 0; var4 < var3; ++var4) { String aAPI_COMPATIBILITY_LIST = var2[var4]; if (requested.startsWith(aAPI_COMPATIBILITY_LIST)) { match = true; } } if (!match) { Reporter.warn("The requested version " + requested + " by your slf4j provider is not compatible with " + Arrays.asList(API_COMPATIBILITY_LIST).toString()); Reporter.warn("See https://www.slf4j.org/codes.html#version_mismatch for further details."); } } catch (Throwable var6) { Throwable e = var6; Reporter.error("Unexpected problem occurred during version sanity check", e); } } private static boolean isAmbiguousProviderList(List<SLF4JServiceProvider> providerList) { return providerList.size() > 1; } private static void reportMultipleBindingAmbiguity(List<SLF4JServiceProvider> providerList) { if (isAmbiguousProviderList(providerList)) { Reporter.warn("Class path contains multiple SLF4J providers."); Iterator var1 = providerList.iterator(); while(var1.hasNext()) { SLF4JServiceProvider provider = (SLF4JServiceProvider)var1.next(); Reporter.warn("Found provider [" + provider + "]"); } Reporter.warn("See https://www.slf4j.org/codes.html#multiple_bindings for an explanation."); } } private static void reportActualBinding(List<SLF4JServiceProvider> providerList) { if (!providerList.isEmpty() && isAmbiguousProviderList(providerList)) { Reporter.info("Actual provider is of type [" + providerList.get(0) + "]"); } } public static Logger getLogger(String name) { ILoggerFactory iLoggerFactory = getILoggerFactory(); return iLoggerFactory.getLogger(name); } public static Logger getLogger(Class<?> clazz) { Logger logger = getLogger(clazz.getName()); if (DETECT_LOGGER_NAME_MISMATCH) { Class<?> autoComputedCallingClass = Util.getCallingClass(); if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) { Reporter.warn(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName())); Reporter.warn("See https://www.slf4j.org/codes.html#loggerNameMismatch for an explanation"); } } return logger; } private static boolean nonMatchingClasses(Class<?> clazz, Class<?> autoComputedCallingClass) { return !autoComputedCallingClass.isAssignableFrom(clazz); } public static ILoggerFactory getILoggerFactory() { return getProvider().getLoggerFactory(); } static SLF4JServiceProvider getProvider() { if (INITIALIZATION_STATE == 0) { Class var0 = LoggerFactory.class; synchronized(LoggerFactory.class) { if (INITIALIZATION_STATE == 0) { INITIALIZATION_STATE = 1; performInitialization(); } } } switch (INITIALIZATION_STATE) { case 1: return SUBST_PROVIDER; case 2: throw new IllegalStateException("org.slf4j.LoggerFactory in failed state. Original exception was thrown EARLIER. See also https://www.slf4j.org/codes.html#unsuccessfulInit"); case 3: return PROVIDER; case 4: return NOP_FALLBACK_SERVICE_PROVIDER; default: throw new IllegalStateException("Unreachable code"); } } }
最新发布
10-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值