WL#4816: PERFORMANCE_SCHEMA SummariesAffects: Server-6.x — Status: Un-Assigned — Priority: LowThe initial description here has been shifted from the "Summaries" section of WL#2360 Performance Schema. This task depends on WL#2360. "Summaries" is not a requirement. Maybe we'll have zero summary tables in 6.1. Therefore this section does not require approval. However, it must be here to show that, with WL#2360 as a basis, we can produce what we'll want. After we finish WL#2360, Peter will shift this description to a new task. [ ADVANCE PSEUDO GROUP BY ] Suppose the EVENTS_WAITS_HISTORY table contained rows, not just for the last 10 events, but for all events since the instance started. Then we could say: SELECT EVENT_NAME, COUNT(*) AS COUNT_STAR, SUM(TIMER_WAIT) AS SUM_TIMER_WAIT FROM EVENTS_WAITS_HISTORY WHERE /* filter_condition, e.g. "in a previous statement" */ GROUP BY EVENT_NAME.GENUS And we'd have a lovely table like this: EVENT_NAME COUNT_STAR SUM_TIMER_WAIT -------- ---------- -------------- Wait/Synch/MUTEX/LOCK_open 14 140 Wait/Synch/MUTEX/LOCK_ha_data 8 80 Wait/Synch/MUTEX/LOCK_logger 22 220 Terminology notes: here EVENT_NAME is the "grouping column", COUNT and SUM are "aggregates", TIMER_WAIT is an "aggregating column", the result set is a "summary table". You'll see those terms several times. All summary information is based on groupings and aggregatings. The only flaw is that it's "pseudo". There is no table that goes back forever. Shortly after producing an EVENTS_WAITS_HISTORY row, we risk overwriting it (see section "Ring Buffers"). So, to provide summary information, we'll have to do the grouping at the same time as, or shortly after, the production of the EVENTS_WAITS_CURRENT row. To some extent, that means we must guess what the user will want. But we can guess well. We can provide a lot of tables for the user to pick from (by enabling in SETUP_CONSUMERS). We can make sure that the tables include anything that's been requested before or provided before for MySQL or for some other DBMS. [ WHAT WE CAN USE FOR AGGREGATING ] These are the possible aggregations, in order of increasing difficulty. COUNT(*) Easy. If there's a row, then ++COUNT_STAR somewhere. SUM(TIMER_WAIT) Easy. Subtract START_TIME from END_TIME (or from current time if END_TIME is still null), and add that to SUM_TIMER_WAIT somewhere. AVG(TIMER_WAIT) Easy. The consumer routine must calculate averages. MIN(TIMER_WAIT) A bit harder. The typical algorithm for calculating or minimum|maximum in a list will have a line like this: MAX(TIMER_WAIT) "if (curr_value < min_value) then min_value = curr_value;" but that might cause a 'jump' instruction at assembly level. If we find that C optimizers use conditional-move instructions rather than jumps, we'll be happier. STDDEV(TIMER_WAIT) Impossible. We won't do equivalents for any of the other MySQL aggregating functions like BIT_XOR, GROUP_CONCAT, STDDEV, and the others listed in the reference manual http://dev.mysql.com/doc/refman/6.0/en/group-by-functions.html because even if we could do them, who would care? TIMER_WAIT is the basis for most aggregations. There were other possibilities: MIN|MAX(START_TIME|END_TIME) if the timer is wall-clock this makes some sense SUM(SPINS) remembering that there might be other 'extra' columns like SPINS. We ignore these other possibilities. [ WHAT WE CAN USE FOR GROUPING ] These are the possible grouping columns: THREAD_ID Actually grouping "by thread" is not a problem because it's implicit. All summarizations are done within a thread. We do not update counters of other threads, and we do not update global counters, except in a special case (see later discussion of "global summaries"). This is one of the reasons we won't need mutexes. EVENT_NAME The components of EVENT_NAME are all candidates for grouping. To answer questions like "how many mutex_locks occurred" or "what is the average time for each type of mutex", we'll need a summary table that has a row for each genus. And genera will be accumulated into orders, which are accumulated into classes, all the way up the taxonomy line. Notice that EVENT_NAME, our favourite grouping column, has a fixed number of possible values and they are all knowable in advance. (Actually a few can be added by storage engines during server initialization but that's still "in advance" for most practical purposes.) This is an important consideration. It means that we can preallocate memory for the grouping permanently, and it means that we can point to the memory quickly, because it's fixed offset. We might get a request for a "per minute" grouping, based on ROUND(TIMER_START/60000000000) or something similar. We won't do it because we can't precalculate the possible values. But it's irrelevant anyway, because groupings by time periods can be done with "Samplings". (See later section "Samplings".) So we won't use any timer columns as grouping columns. We might get a request for a grouping based on OBJECT_INSTANCE_BEGIN. This would be great for answering really detailed questions like "how many times did somebody access row#5" or "what was the time elapsed when we wrote to a file starting at byte#16384". Sorry, there are just too many possible values, Nobody saw a way to do it. Histograms for OBJECT_INSTANCE_BEGIN might be a substitute, but we won't have them either. I'll talk later about "per statement" and "per object" groupings. I think it's unlikely that people would want a grouping by EVENT_ID or SPINS. And when something's unlikely, it's out of the question. Combining the considerations about aggregating and grouping, we know therefore there is an 'aggregators' struc which will contain these fields: NAME LENGTH DESCRIPTION grouping_type 2 Identifies genus or other category from event_name count_star 8 We ++count_star whenever we end a wait sum_time_elapsed 8 We add to sum_time_elapsed when we end a wait min_time_elapsed 8 We calculate min_time_elapsed when we end a wait max_time_elapsed 8 We calculate max_time_elapsed when we end a wait Total length = 42 bytes for one aggregators-struc occurrence. [ DEFINITION OF A TYPICAL TABLE ] Table name = EVENTS_WAITS_SUMMARY_BY_THREAD_BY_GENUS List of columns and data types: THREAD_ID BIGINT COUNT_STAR BIGINT EVENT_NAME VARCHAR(128) SUM_TIMER_WAIT BIGINT MIN_TIMER_WAIT BIGINT MAX_TIMER_WAIT BIGINT AVG_TIMER_WAIT BIGINT Compare the definition of EVENTS_WAITS_CURRENT: columns EVENT_ID SPINS OBJECT_SCHEMA OBJECT_NAME OBJECT_TYPE OBJECT_INSTANCE_BEGIN are gone, column TIMER_WAIT is summarized, column COUNT_STAR is added. [ NESTING-EVENT SUMMARIES ] A "statement" might cause multiple "waits" or "file" events. So we say that "wait" or "file" events can be "nested" inside "statements". There are several levels of nesting in classes of events, for example Transaction -> Statement -> Stage (e.g. "sort") -> Wait. So far there's no firm definition of the classes and nestings, so let's just posit that they exist, and that "statement" can be a nesting event for a "wait". It's common to require grouping by a nesting event. For example kostja_notes.txt (attached to WL#2360) asks for "Example requirements: - wait time accumulated per SQL statement - physical reads per SQL statement" And the support wishlist [mysql intranet]/secure/wiki/ServerMonitoringWishlist asks for "reads per statement". However, we shouldn't use "statement id" as a grouping column for reasons mentioned in the "what we can use for grouping" subsection, above. So this case requires special handling. Our rule will be: You must add to the aggregators for a nesting event, as well as your own. Luckily the nesting event is easy to find because each thread, at any given moment, has only one running top-level statement, one running stage, one running wait, etc. No table lookup is necessary. The performance-struc for a statement event will have additional fields for these aggregators, as well as its own: NAME LENGTH DESCRIPTION thread_id ? See earlier section. event_id 8 See earlier section. event_name 8 See earlier section. timer_start 8 See earlier section. timer_end 8 See earlier section. spins 4 See earlier section. object_id 8 See earlier section. object_instance_begin 8 See earlier section. nested_1_level_below 80*42 occurrences of 'aggregators' struc for stages nested_2_levels_below 80*42 occurrences of 'aggregators' struc for waits The additional "nested_*" fields will be reflected in the EVENTS_STATEMENTS_CURRENT and EVENTS_STATEMENTS_HISTORY tables. To save space and time, the additional "nested_*" fields will have only a small number of occurrences. There will be an occurrence for each "family", not for each "genus". (That's why I say LENGTH is 80*42: guessing 80 families and 42 bytes per aggregator struc.) In the name "wait/synch/mutex/lock_open" the family is "mutex" and the genus is "lock_open". So what the user will see is SELECT * FROM EVENTS_STATEMENTS_CURRENT\G ... /* all the columns described for EVENTS_WAITS_CURRENT, above */ WAIT_SYNCH_MUTEX_COUNT_STAR WAIT_SYNCH_MUTEX_SUM_TIMER_WAIT WAIT_SYNCH_MUTEX_MIN_TIMER_WAIT WAIT_SYNCH_MUTEX_AVG_TIMER_WAIT WAIT_IO_FILE_READ_COUNT_STAR etc. So, at the cost of a lot of memory, we have something that shows detail statistics for every stage of a statement (which means that SHOW PROFILE is redundant), and for every class of wait of a statement, for the last 10 statements. In addition, of course, we will have the statement's own aggregations which are updated when the statement ends. (I've said elsewhere that the EVENTS_STATEMENTS_ tables look pretty much the same as the EVENTS_WAITS_ tables, so there is a table EVENTS_STATEMENTS_HISTORY with 10 rows in it.) (By the way, there might be a big difference between statement's total time and accumulated time of nested events. That's because the "time" for any event includes time swapped out. For a wait, that's what you want anyway. And anyway the real work might be done by a backgrounder. And not every bit of code within a statement is instrumented. Nesting-event summaries are independently stored, so they may be enabled even if the lower-level summaries are disabled. [ HISTOGRAMS ] The proposal is "no histograms", but let us consider them. A histogram, like COUNT/SUM/AVG/MIN/MAX, is a type of aggregation. So as well as COUNT(*), we could have HISTOGRAM_COUNT(TIMER_WAIT). It answers the question "how many times did an instrument take less than x seconds, how many times did it take less than y seconds, how many times did it take less than z seconds, and so on". So it returns multiple integers showing counts for ranges of times. Possible alternate word: "quantize". As far as I know, nobody has asked MySQL for summaries with histograms. But histograms are known to be an excellent way to present information, so there are precedents outside the DBMS industry. We will use exponential powers-of-2 buckets. Reasons: (a) bit scanning can be quick on some processors, for example on x86-64 you can find out "what is the index of the most significant bit" with a single instruction, BSR (b) we won't have to figure out in advance what the ranges are Posit 8 buckets for < 256, < 1024, < 4096, < 16384, < 65536, < 262144, < 1048576, >= 1048576. Some users will want fixed-width buckets (0 - 999, 1000 - 1999, 2000 - 2999, etc.) but that is more trouble (although it would require a less exotic instruction than BSR, since this is a case for integer division). There is a table EVENTS_WAITS_SUMMARY_WITH_HISTOGRAMS. It has the same columns as EVENTS_WAITS_SUMMARY, and 8 additional columns: HISTOGRAM_COUNT_TIME_ELAPSED_LESS_THAN_256 BIGINT, HISTOGRAM_COUNT_TIME_ELAPSED_LESS_THAN_1024 BIGINT, HISTOGRAM_COUNT_TIME_ELAPSED_LESS_THAN_4096 BIGINT, HISTOGRAM_COUNT_TIME_ELAPSED_LESS_THAN_16384 BIGINT, HISTOGRAM_COUNT_TIME_ELAPSED_LESS_THAN_65536 BIGINT, HISTOGRAM_COUNT_TIME_ELAPSED_LESS_THAN_262144 BIGINT, HISTOGRAM_COUNT_TIME_ELAPSED_LESS_THAN_1048576 BIGINT, HISTOGRAM_COUNT_TIME_ELAPSED_GREATER_THAN_OR_EQUAL_TO_1048576 BIGINT For example, suppose that we have read a row 5 times. Twice the operation took 8000 canonical time units (pseudo-picoseconds), three times it took 50000 canonical time units. The statement SELECT * FROM EVENTS_WAITS_SUMMARY_WITH_HISTOGRAMS WHERE EVENT_NAME LIKE 'FILE/IO/READ%'\G will return EVENT_NAME FILE/IO/READ ... COUNT_STAR 5 SUM_TIME_ELAPSED 166000 ... HISTOGRAM_COUNT_TIME_ELAPSED_LESS_THAN_16384 2 HiSTOGRAM_COUNT_TIME_ELAPSED_LESS_THAN_65536 3 ... This looks useful. So why is the proposal to reject histograms? Well, partly because it's a bunch of work to make it fast (I doubt that optimizers will use the right instructions). More importantly because it's a bunch of memory, even if we use only 8 buckets that's 64 bytes per summary row, and assume 500 summary rows per thread. It's true that we don't have to use that space if we disable the histograms tables in SETUP_CONSUMERS, but almost all users would disable, so almost no users would benefit from histogram support. [ OBJECT SUMMARIES ] A grouping "by object" is what people are asking for when they say "* Track blocks read per file * Track blocks written per file * Track I/O per second average per file" [mysql inside intranet address]/wiki/ServerMonitoringWishlist And one of the Google patches can give counts of reads and writes per table, or per index. I've also heard a request for "how often has a procedure been called". Generally we want to summarize for "database objects" (base tables, indexes, stored procedures, functions, events, triggers, modules, constraints) or named "session objects" (prepared statements, cursors, conditions, variables, savepoints). We don't worry about summarizing for things like mutexes because they'd be both easy and unnecessary. We can get summaries for all the lock_open mutexes without needing to know the address that was used for lock_open. At first glance it looks simple: just declare that OBJECT_NAME etc. are grouping columns, or do the same things that we did for "nesting-event summaries". But both those methods fail because: * the number of objects isn't fixed and might be large * the location of the object struc isn't known, it must be looked up * the object name columns are strings, so they take time to compare. So we need WL#2760."Super-Database (real Data Dictionary)". I'm assuming that WL#2760 will give us persistent in-memory descriptors for all schema objects, so we'll be able to use an address or offset whenever we're using the object. When we have that, object summaries will be easy. However, WL#2760 will not be available until MySQL 6.2. In the meantime, we could offer a restricted-functionality solution. This works only for base tables in the table cache. The person doing the instrumenting has to pass the address of the table descriptor. We don't actually store the summary information in the table descriptor, but we need it as a reference point for the persistent information. First: we have a new SETUP table. Call it SETUP_OBJECTS. It contains: OBJECT_TYPE VARCHAR(64), SCHEMA NAME VARCHAR(64), OBJECT_NAME VARCHAR(64), ENABLED ENUM('Y','N'), AGGREGATED ('Y','N'). Initially this table is empty. A user can say INSERT INTO SETUP_OBJECTS('TABLE','TEST','T','Y','Y'); ('TABLE' meaning base table is the only possible type but the TYPE column is just here for future use.) This means, essentially, that we want to enable object summarizing for table test.t. Second: in the per-thread memory, we have an area for object summaries. For one object, it contains: schema name object name i.e. table name pointer to table cache multiple occurrences of 'aggregator' struc The number of occurrences is fixed for an instance, it depends on a mysqld server-startup-option. Thus there is a limit to the number of tables that one can insert into SETUP_OBJECTS. Also the number of occurrences will depend on the number of 'families', but in the case of a table there are only four families: read_row, update_row, delete_row, write_row. So we're looking at approximately 128 + 128 + 8 + 42 * 500 * 4 = 85000 bytes per thread, if we allow for aggregating of 500 tables. Third: we change the instrumentation requirement so that, for base tables, the person writing the instrument must specify the "object name" in a way so that we can interpret it as a reference to the table cache, rather than a string. This may be stored in 'performance-struc' as well. Fourth: the instrument end-timer ("end_wait") for tables will differ from the instrument end-timer for other objects. It will check: is the table identifier equal to one of the 'pointer to table cache' items for the thread? (This is a simple integer comparison but it might be repeated 500 times, so there is variable extra overhead for table summarizing.) If so, and the enabled and aggregating flags are on, then MySQL adds to the aggregator fields, in the same manner as when it adds to nesting-event aggregator fields. Fifth: there are summary tables, OBJECTS_SUMMMARY_BY_THREAD_BY_TYPE and OBJECTS_SUMMARY_GLOBAL_BY_TYPE. They'll contain the usual sort of summary columns (COUNT_STAR, SUM_TIMER_WAIT, etc.). By searching the global table, assuming the instrument points are around read/write/update/delete for rows, you'll have the counting and timing for all DML done on the table. There is no 'history' table for objects, and the counts just keep going up because this description doesn't allow for resetting the counts. The implementor will doubtless have some trouble with robustness, given that users might be updating SETUP_OBJECTS, and the contents of the table cache might be changing, while aggregating occurs. Once again, we will just say: very occasionally there will be erroneous information, we just document that's the way things are. Once WL#2760 is done and we add support for other object types besides 'TABLE', very little will change from the user's point of view. The table and column descriptions will be the same. [ INFINITE HISTORIES ] The proposal is "no infinite histories", but let's consider them. At the outset, I said that we have to update summaries during the instrumentation because "There is no table that goes back forever." But what if there was? There are two possibilities: * Add 3GB of memory to your machine. Now there is enough space for an EVENTS_WAITS_HISTORY_LONG_LONG_LONG table. * Enable file trace. Now there is a disk file instead of memory. We can convert it to a MySQL table. With such scenarios, the instrumentation doesn't need to do any summarizing at all. We can assume that users will do their own ad-hoc grouping and aggregating for everything. But, if we require lots of memory, we make the instrumentation obtrusive. Our objective is to make all users comfortable with monitoring always on. And trace files have high overhead, as mentioned elsewhere (WL#4878). So assume there are no infinite histories. [ HIGHER-LEVEL SUMMARIES ] Earlier we said that there are multiple summary tables, each for a different EVENTS_NAME level, for example EVENTS_WAITS_SUMMARY_BY_FAMILY and EVENTS_WAITS_SUMMARY_BY_GENUS. but there is only one underlying structure, the summaries for "by genus". For all tables above the genus level (family, order, class, etc.), the instrumentation does nothing at all. We know that the Consumer can calculate the per-family summaries by adding up the per-genus summaries. Higher-level summaries are calculated, so they're disabled if the lower-level summaries are disabled. [ GLOBAL SUMMARIES ] A global summary would be processed thus: There are also occurrences of performance-structure-summary in global memory. But these global summaries are not totals for all current jobs. They are only for non-current jobs, that is, jobs that have terminated. Why don't we update the global summaries at the same time as the per-thread summaries? Because we fear conflicts, with two threads updating the global summaries at the same time. That might be harmless if the only operation is ADD. But the second reason is that we want the Provider's End procedure ("end_wait") to be as fast as possible, and so we'll pass extra work to the Consumer. When the Provider thread is about to terminate, it gets a mutex (call it Terminator mutex) and then adds all per-thread summaries to the global summaries. When a Consumer thread is about to display global information, for example "the total number of mutex acquisitions for all sessions since the server started", it must go through all the per-thread summaries and add them in its temporary storage, and then add the global summaries. So the important things to understand are that all summmaries are done "within a thread", thus removing contention problems, which is important because Providers never use mutexes. But if a thread is killed and its memory is deallocated, then all its tables re copied to a single "global summaries" area. This copying has to be under mutex control, and Consumers must see the mutex, but it's not an exception to our "Providers never use mutexes" rules. I mean the instrumentation has no mutexes. This does mean that monitoring a dead thread is impossible. But we have merely decided that information about dead threads is less important, so it ends up in summaries, samples, and user-generated histograms. Its details disappear. The consumer of a "global" summary table will have to go through all the threads and add them up, and then add the summaries from the dead-threads global area. Global summaries are calculated, so they're disabled if the by-thread summaries are disabled. [ THE ROUTINE ] Simply put, a per-thread summary would be processed thus: In the End procedure ("end_wait"), the Provider takes values from performance-structure and adds them to a performance-structure-aggregator. The summary is "by class of event", still within the thread. For example, when we finish a mutex call, we calculate the total canonical time units waited (.timer_end - .timer_start) and add it to performance-structure-aggregators [mutex] . sum_timer_wait. And we increment performance-structure-summary [mutex] . count_star. We will need this summary for another table, too. Remember that intrumentation has a 'start' (before the instrumented code) and an 'end' (after the instrumented code). All summarizing is in the 'end' ("end_wait"). The pseudocode for a WAIT instrumentation is: /* Do the non-summary instrumentation code. */ if (summary table for statement is enabled) ++statement.summary.count_star; statement.summary.sum_elapsed+=elapsed-time-for-this-instrument; conditional-move(statement.summary.min_elapsed,elapsed-time-for-this-instrument); conditional-move(statement.summary.max_elapsed,elapsed-time-for-this-instrument); if (summary table for this wait genus is enabled) ++wait.summary[genus].count_star; wait.summary[genus].sum_elapsed+=elapsed-time-for-this-instrument; conditional-move(wait.summary[genus].min_elapsed,elapsed-time-for-this-instrument); conditional-move(wait.summary[genus].max_elapsed,elapsed-time-for-this-instrument); In assembler, with wonderful optimization, with all summaries enabled, this will take 2 not-taken conditional-jump instructions, 4 increment or add instructions, 1 multiplication to get canonical time units from the units that the timer returns, 4 conditional moves, and 1/5 cache miss. 55 cycles. Eventually there will be 2 other nesting-event classes besides 'statements', so multiply by 2, and say 110 cycles. This is a good time to estimate the total cost. It's 110 cycles overhead in speed terms, and in memory terms it's 42 bytes per 'aggregators' struc times 500 occurrences of 'aggregators' in genus-level summaries plus 2 occurrences of 'aggregators' in statement or stage summaries times 200 threads plus 500 occurrences of 'aggregators' in global summaries Typically, then, it will all add up to somewhere around 1MB. Our cost estimate, then, is 110 extra cycles per instrument and 1MB extra memory. [ TRICKS ] I think that the cost is acceptable on all possible platforms. But let's think about a few ways to reduce. These are tricks; they are recommendations; they are not requirements. 1. Be cheap with SETUP_CONSUMERS. Effectively SETUP_CONSUMERS acts as a filter on production of summary tables. Disabling saves a tiny amount of time, but more importantly, it can save memory. Suppose that there are 1000 summary tables, but we have a fixed limit, we say that users cannot enable more than 100. Now, allocate memory for only 100 strucs, and one list of pointers from the enabled-summaries list to the strucs themselves. 2. Summarize 1 time for last 10 performance-strucs, instead of 10 times for laat 1 performance-strucs. The current proposal is that the summary happens for every instrumentation, during end_timer. But if we summarized every tenth time, and went through all 10 rows in events_waits_history, then we wouldn't lose any information. This won't reduce the number of calculations for the summaries themselves. But it will reduce the number of checks of enabled flags, and it makes cache hits more probable. 3. Use 32-bit not 64-bit fields. Usually COUNT_STAR and SPINS won't overflow for several days. So we could use less memory if we didn't have 64-bit fields for every summary item. 4. Group by family not by genus. This is just because we might have too many genera (I'm estimating 500 genera). So another way to accomplish the same purpose is to disable instrumenting for many genera, permanently. [ EXAMPLES ] Assuming default schema = performance schema. "What is the total number of wait calls of any type since instance start?" SELECT COUNT_STAR FROM EVENTS_WAITS_SUMMARY_GLOBAL; "For the current statement on thread 17, show the statement elapsed time, the total time spent in the last 10 mutexes, and the average time spent on all mutexes of type 'lock_open'". SELECT TIMER_WAIT FROM EVENTS_STATEMENTS_CURRENT WHERE THREAD_ID = 17 UNION ALL SELECT SUM(TIMER_WAIT) FROM EVENTS_WAITS_HISTORY WHERE THREAD_ID = 17 UNION ALL SELECT WAIT_SYNCH_MUTEX_AVG_TIMER_WAIT FROM EVENTS_STATEMENTS_CURRENT WHERE WAIT_SYNCH_MUTEX_EVENT_NAME = 'Wait/Synch/Mutex/Lock_open'; "Show contents of all waits summary tables." SELECT * FROM EVENTS_WAITS_SUMMARY_BY_THREAD_BY_CLASS; SELECT * FROM EVENTS_WAITS_SUMMARY_BY_THREAD_BY_ORDER; SELECT * FROM EVENTS_WAITS_SUMMARY_BY_THREAD_BY_FAMILY; SELECT * FROM EVENTS_WAITS_SUMMARY_BY_THREAD_BY_GENUS; ... SELECT * FROM EVENTS_LOCKS_SUMMARY_BY_THREAD_BY_CLASS; ... SELECT * FROM EVENTS_STAGES_SUMMARY_BY_THREAD_BY_CLASS; ... SELECT * FROM EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_CLASS; ... SELECT * FROM EVENTS_WAITS_SUMMARY_GLOBAL_BY_CLASS; SELECT * FROM EVENTS_WAITS_SUMMARY_GLOBAL_BY_ORDER; SELECT * FROM EVENTS_WAITS_SUMMARY_GLOBAL_BY_FAMILY; SELECT * FROM EVENTS_WAITS_SUMMARY_GLOBAL_BY_GENUS; ... SELECT * FROM EVENTS_LOCKS_SUMMARY_GLOBAL_BY_CLASS; ... SELECT * FROM EVENTS_STAGES_SUMMARY_GLOBAL_BY_CLASS; ... SELECT * FROM EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_CLASS; /* several hundred tables in all, depending how many classes we make */ No Comments yet |
VotesWatches0 members are watching this worklog
You must be logged in to track this worklog.
Provide Feedback
You must be logged in to comment
|