SQL_EXEC_ID分析(转帖)
SQL_EXEC_ID--SQL Execution ID ,是Oracle11G新加入到v$session中的一列,与SQL_ID和SQL_EXEC_START共同组成了execution key,execution key能够唯一地辨别出同一SQL语句的两次执行。
对于不同的SQL语句的执行,通过SQL_ID可以区分;对于同一SQL语句(SQL_ID相同)的执行,需要通过SQL_EXEC_START和SQL_EXEC_ID区分。
1、同一语句,在同一实例上,每执行一次,SQL_EXEC_ID增加1,与执行的用户和session无关。
Session1: 同一SQL语句执行两次
在同一session中执行同一SQL语句两次,SQL_ID不变,而SQL_EXEC_ID增加1。
Session2: 在另一会话中用不同用户执行同一SQL语句
在另一个session中使用另外的用户执行同样的SQL语句,SQL_ID不变,而SQL_EXEC_ID在原来Session1的基础上再增加1。
再回到session1 继续执行相同的SQL语句:
Session 1:
SQL_EXEC_ID又在Session2的基础上增加1。
这说明SQL_EXEC_ID与执行用户和会话均无关,只与SQL的执行次数相关。(此结论只有在SQL语句在同一实例上执行时成立)
2、数据库重启后,SQL_EXEC_ID会重置。
Session 1: 重启数据库,然后执行同一SQL语句。
重启数据库后,再次执行同一SQL语句时,SQL_EXEC_ID的值被重置了,又回到了初始值16777216,即0x1000000。这时用于区别同一SQL语句的执行的是列SQL_EXEC_START(SQL_EXEC_ID和SQL_ID值都与Session1的第一次执行相同)。
关于SQL_EXEC_ID的结构和含义请参考:
http://blog.tanelpoder.com/2011/10/24/what-the-heck-is-the-sql-execution-id-sql_exec_id/?utm_source=rss&utm_medium=rss&utm_campaign=what-the-heck-is-the-sql-execution-id-sql_exec_id
What the heck is the SQL Execution ID – SQL_EXEC_ID?
Ok, I think it’s time to write another blog entry. I’ve been traveling and dealing with jetlag from 10-hour time difference, then traveling some more, spoken at conferences, drank beer, had fun, then traveled some more, trained customers, hacked some Exadatas and now I’m back home.
Anyway, do you know what is the SQL_EXEC_ID in V$SESSION and ASH views?
Oh yeah, it’s the “SQL Execution ID” just like the documentation says … all clear. Um … is it? I’d like to know more about it – what does it actually stand for?! Is it session level, instance level or a RAC-global counter? And why does it start from 16 million, not 1?
SQL> SELECT sql_exec_id FROM v$session WHERE sid = USERENV('SID'); SQL_EXEC_ID ----------- 16777216
This number 16777216 looks strangely familiar – indeed, it’s 2^24.
When I run the same query again (incrementing the SQL_EXEC_ID counter for the same SQL), I see the counter going up by 1:
SQL> SELECT sql_exec_id FROM v$session WHERE sid = USERENV('SID'); SQL_EXEC_ID ----------- 16777217 SQL> SELECT sql_exec_id FROM v$session WHERE sid = USERENV('SID'); SQL_EXEC_ID ----------- 16777218 SQL> SELECT sql_exec_id FROM v$session WHERE sid = USERENV('SID'); SQL_EXEC_ID ----------- 16777219 SQL> SELECT sql_exec_id FROM v$session WHERE sid = USERENV('SID'); SQL_EXEC_ID ----------- 16777220
Further executions of the same query keep incrementing this counter, one by one – even if I run this same SQL from another session in the same instance. So, this SQL_EXEC_ID is not a session-scope value for each SQL_ID, it’s at least instance-wide. It looks like the counting starts from 2^24 (the bit representing 2^24 is set) and ignoring that bit for now, the counting works normally, one by one, starting from zero.
Note that changing even a single character in the SQL text (see the extra space in the end before the semi-colon) causes the SQL_ID to change and a different SQL_EXEC_ID counter to be reported (which starts from “zero” again). A separate SQL_EXEC_ID counter is maintained in shared pool for each SQL_ID:
SQL> SELECT sql_exec_id FROM v$session WHERE sid = USERENV('SID') ; SQL_EXEC_ID ----------- 16777216
So, obviously, when I have just restarted my instance and still see 16777216 as the starting SQL_EXEC_ID for any SQL I execute, it must mean that the full SQL_EXEC_ID value contains something else than just the execution number of this SQL_ID. Whenever I see such familiar values (like powers of 2), then I like to look into the values in hex format to see whether some higher order bits are used for some special purpose. Let’s run a new SQL statement:
SQL> SELECT sql_exec_id, TO_CHAR(sql_exec_id,'XXXXXXXX') hex FROM v$session WHERE sid = USERENV('SID') ; SQL_EXEC_ID HEX ----------- --------- 16777216 1000000 SQL> SELECT sql_exec_id, TO_CHAR(sql_exec_id,'XXXXXXXX') hex FROM v$session WHERE sid = USERENV('SID') ; SQL_EXEC_ID HEX ----------- --------- 16777217 1000001 SQL> SELECT sql_exec_id, TO_CHAR(sql_exec_id,'XXXXXXXX') hex FROM v$session WHERE sid = USERENV('SID') ; SQL_EXEC_ID HEX ----------- --------- 16777218 1000002
Indeed, it looks like the 25th bit (2^24) is always pre-set to 1, while the least significant 24 bits represent how many times this SQL ID has been executed in an instance (I have tested this with a loop – the 24 least significant bits do get used fully for representing the SQL ID’s execution count in the instance and once it reaches 0xFFFFFF – or 0x1FFFFFF with that pre-set 25th bit, it wraps to 0×1000000 – the 25th bit still remaining set!). So the SQL_EXEC_ID can reliably only track 2^24 – 1 SQL executions in an instance and then the counter wraps to beginning. This is why you should include SQL_EXEC_START (date datatype with 1 sec precision) column in your performance monitoring queries as well, to distinguish between SQL executions with a colliding SQL_EXEC_ID. As long as you’re executing your SQL statement less than 16.7 million times per second per instance, this should be fine :-)
Anyway, so what’s the magic 25th bit then? Well, in RAC it would be very hard to somehow coordinate the incrementing of a single counter globally (that’s why you want to keep your sequences cached in RAC), I figure that there are different counters for the same SQL ID in different RAC instances. Let’s check – I will log in to another RAC node (node 2) and run this:
SQL> SELECT sql_exec_id, TO_CHAR(sql_exec_id,'XXXXXXXX') hex FROM v$session WHERE sid = USERENV('SID') ; SQL_EXEC_ID HEX ----------- --------- 33554433 2000001 SQL> SELECT sql_exec_id, TO_CHAR(sql_exec_id,'XXXXXXXX') hex FROM v$session WHERE sid = USERENV('SID') ; SQL_EXEC_ID HEX ----------- --------- 33554434 2000002 SQL> SELECT sql_exec_id, TO_CHAR(sql_exec_id,'XXXXXXXX') hex FROM v$session WHERE sid = USERENV('SID') ; SQL_EXEC_ID HEX ----------- --------- 33554435 2000003
Whoa – the SQL Execution ID in the 2nd instance starts from 33 Million! And when you convert the value to hex, you’ll see that now the 26th bit is set – showing that this SQL was executed in instance #2!
So, it very much looks like that while the 24 least significant bits are used for the SQL execution ID counter, the more significant bits are used for showing which instance_id ran that SQL. Assuming that 32 bits are used for the whole SQL_EXEC_ID value, then up to 8 higher order bits could be used for storing the instance_id – supporting up to 256-node RAC clusters. This is very useful when analyzing past ASH data as you can aggregate data (count min/max exec ID difference to get the execution counts in a time range) either in each separate instance or globally – by stripping out the instance_id part from the value.
I haven’t tested the instance_id part with 256-node RAC clusters (as Santa Claus is cutting back due to poor economy), but at least on an 8-node full rack Exadata all 8 instance_ids were reported properly. Note that for serial queries, the SQL_EXEC_ID shows you the instance_id of the instance where the session is logged on to, but for inter-instance parallel query, you will see the instance_id of the query coordinator for all PX slaves, regardless of in which instances they run. Here’s a little script from a 8-node Exadata cluster to show it. I’ll leave it up to you to fully figure it out what, how and why it’s doing, but basically what it shows is that the SQL_EXEC_ID consists of the query coordinator’s instance_id value and the execution number for a SQL_ID in the instance where the query coordinator session was logged in:
SQL> SELECT qc_instance_id, MIN(TO_CHAR(sql_exec_id,'XXXXXXXX')) 2 , MAX(TO_CHAR(sql_exec_id,'XXXXXXXX')) 3* FROM gv$active_session_history GROUP BY qc_instance_id order by 1 SQL> / QC_INSTANCE_ID MIN(TO_CH MAX(TO_CH -------------- --------- --------- 1 1000000 100540F 2 2000000 20009BF 3 3000000 300541E 4 4000000 40000DD 5 5000000 50C5035 6 6000000 600018C 7 7000000 700023D 8 8000000 8000755 1000000 803DF3B 9 rows selected.
That’s all for today – more cool stuff is coming, I promise :-)