What Next ? All about sequences. Avoiding the temptation to rattle on about some of the wonderful new features of Oracle 8.1, this article focuses on SEQUENCES . Although sequences have been a part of Oracle for many years, it is surprising how many misconceptions are still floating around the community about how they work. This article aims to be your definitive guide to sequences for all versions of Oracle at least from 7.3.3 to 8.1.5. What is a Sequence ? There are two answers to this question, or rather there are really two questions: what does a sequence look like to to a user, and what does a sequence look like internally to Oracle. A sequence is an object that allows a user to call for a meaningless number with the knowledge that no other user will ever be given the same number (except for the special case of 'CYCLE' sequences) and with the benefit that there will be virtually no delay before the number becomes available. In most cases an Oracle sequence object behaves as you would expect a ‘real-life’ sequence number to behave: each time you say ‘next please’ the next consecutive integer appears; however there are many variations to sequences that make this association very mis-leading. Internally a sequence consists of a row in the seq$ dictionary table that records its definition and current high-water mark, and a cached component in the SGA (seen through the dynamic performance view v$_sequence) which repeats this information and also holds the next value to be supplied to an end-user request. The SGA cache controls the values passed out in response to user requests for sequences, and updates the data dictionary with high-water marks from time to time, so there is a point at which serialisation can occur (more on this later). Because the mechanism is internal to Oracle it is very efficient and bypasses the normal locking contention that appears with the traditional end-user coded ‘tables of sequence values’ that had to be implemented in very early versions of Oracle. Creating a Sequence: The full syntax for creating a sequence is shown in fig 1. Create sequence XXX Start with {integer} increment by {integer} maxvalue {integer} / nomaxvalue minvalue {integer} / nominvalue cycle / nocycle cache {integer} / nocache order / noorder Figure 1: Syntax to create a sequence But the shortest code you can use to create a sequence is simply: create sequence my_seq; This will result in a sequence with the default values as shown in fig.2 - In general it is a bad idea to create sequences like this, as it can lead to performance problems. Fortunately there is an ‘alter sequence’ command that allows you to change all aspects of a sequence apart from the starting value. There are some fairly obvious limits to the ‘alter sequence’ command: you may not, for example, alter the maxvalue to be a value smaller than the current value of the sequence. Create sequence my_seq Start with 1 increment by 1 maxvalue nomaxvalue minvalue 1 nocycle cache 20 noorder select my_seq.nextval from dual; select my_seq.nextval into :m_variable from dual; insert into parent_tab values (my_seq.nextval); insert into child_tab select my_seq.currval from big_table; Figure 4 Legal use of nextval/currval Figure 2 The defaults for 'create sequence' Using a Sequence: There are only two requests you can make to a sequence object: ‘give me the next available number’ and ‘remind me what that was again’; these are the nextval and currval calls respectively. The nextval request goes to the global cache to get the next available sequence value and copies it to the session’s local memory. The currval request goes back to the session’s local memory and repeats the value it finds there. An important point to remember when using the nextval / currval cycle is that a session cannot call currval before its first call to nextval otherwise the session gets Oracle error 8002 : sequence XXX.CURRVAL is not yet defined in this session. There are some restrictions on the exact use of the nextval and currval calls. Loosely speaking, the call must be associated with an SQL statement, although some uses (e.g. in subqueries, in snapshots, in aggregate queries) are invalid. Fig 3 gives a few examples of legal use, whilst fig. 4 shows the most common invalid use of nextval/currval. Problems with Sequences: declare m_var number; begin m_var := jpl_demo.nextval; end; pls-00357: (not allowed in this context) Figure 3 Illegal use of nextval/currval Perhaps the commonest use for a sequence is to generate a meaningless (surrogate) primary key. Sequences seem to be perfect for this: no-one gets the same value, there is (usually) no contention for sequence values, and the parent/child association can be handled easily through the nextval/currval calls. Prior to the introduction of sequences, developers would create a table of the form (object_name, seq_no), and execute code similar to the sample in fig. 5. The problem with this strategy is that it serialises the creation of data – the user who gets a sequence number from a sequence table is locking that row and stopping anyone else from getting a value for the same sequence. This serialisation is probably the reason why Oracle introduced its sequences. An Oracle sequence type is not associated with a transaction, so one session does not have to wait for another to commit before acquiring the next available value. But this feature of sequences leads to the commonest complaint about the way they behave – sequence numbers can be lost. Losing Sequence Numbers There are three ways to lose sequence numbers. The first is to throw them away, the second is to lose them in cache flushing, the third is to lose the entire SGA when the database aborts. Remember that the ‘active’ part of the sequence is cached in the SGA, and consists of the definition, the current high-water mark, and the next value to be allocated. If your session requests a sequence number (as the primary key for a new row perhaps), then ‘the next value’ is incremented. If you choose to do nothing with that value (or perhaps rollback the transaction that was going to use it) then there is no mechanism for ‘putting the value back’. The sequence number simply disappears - unlike the equivalent action on a ‘sequence table’ where a rollback would undo the change and make the previous value visible again. If the database crashes (or you issue a shutdown abort) then any values between ‘the next value’ and the high-water mark are lost. In fact earlier versions of Oracle would lose select seq_no + 1 from seq_table where object_name = ‘XXX’ for update of seq_no nowait; update seq_table set seq_no = seq_no + 1 where object_name = ‘XXX’; Figure 5 Sequences through tables these values even during an immediate or normal shutdown, it is only relatively recently that the number cached as ‘the next value’ was written back to seq$ as the latest high-water mark. Finally if the SGA caching area for sequences (defined by the init.ora parameter sequence_cache_entries) is too small then a sequence could be flushed from the SGA and lose all the values between ‘the next value’ and the high-water mark. In fact I have found that this init.ora parameter is not a strict limit in 8.0, and is hidden anyway in 8.1. For version 7, though, you may as well set this parameter larger than the maximum number of sequences used in the database. Some developers think that they are losing sequences numbers because calls to nextval do not give them the value they expect. Of course, since the sequence is centrally cached in the SGA, all that has happened is that another session has been asking for the same sequence at the same time. Another odd feature of sequences and ‘missing’ sequence numbers appears in the Oracle Parallel Server. Each instance holds it own cache of sequence values – so in a 3-instance system, you may find instance A has cached 1 to 20, instance B has cached 21 to 40, and instance C has cache 41 to 60, with the high-water mark in seq$ necessarily set to 60. If instance A is the busiest user of the sequence, then it may the first instance to hit its cached high-water mark (20). At this point it reads seq$, bumps the database high-water mark to 80, and sets its own cache to hold values 61 to 80. Not only is this a big surprise to the user on instance A, but also many values between 20 and 60 could be lost if instances B and C now shut down. Performance Issues Oracle sequences are much faster than user-defined sequences but they still have an inherent performance limitation – to ensure uniqueness, the database has to hold a highwater mark. The speed of sequences is largely dependent on the frequency with which this highwater mark is updated, and in high-performance system you need to think about this carefully. The default CACHE value for a sequence is 20, which means that after every 20 calls for nextval Oracle has to update seq$ and refresh its cache. If you are creating data at a very high rate (e.g. create table as select with a sequence number for the primary key) this update of seq$ (with its rollback and redo logging) can become the most expensive part of the operation. As a demonstration, fig. 6 shows a simple CTAS statement, and a list showing the effects on the execution time of changing the cache size. The effects of a small CACHE size on a Parallel Server system are, of course, much worse since the block containing the relevant row from seq$ will be pinged from instance to instance as the row is updated. (And this will affect at the very least all the other sequences in the same block). Create table jpl_ctas unrecoverable as select jpl_demo.nextval from big_table where rownum <= 10000 Cache Size 1,000 20 nocache Time (secs) 1.602 3.38 35.30 Figure 6 Effects of different CACHE sizes Clearly then, it is important to set a large CACHE value - but there is another trap on Parallel Server systems in the ORDER / NOORDER option. The purpose of this setting is to ensure that sequence values are assigned in order of the request (not that I have ever seen sequence numbers allocated out of order); but I have already pointed out that in Parallel Server systems each instance gets its own range of values. So how does Oracle manage to hand out sequence numbers in order if each instance has its own set of values ? The answer is - they don’t. For parallel server systems ORDER and CACHE are incompatible options, and the sequence is NOT CACHED which could result in an awful lot of pinging. Conclusion: I said in the heading for this article that I wanted to provide the definitive guide to sequences. Perhaps that was a little optimistic for 2,000 words. I have not covered descending sequences, uses of CYCLIC sequences, the standard trick to reset a sequence and many other features, but I hope that I have made 6 points clear. Each sequence has a small, instance-global cache of values to be distributed when nextval is called. You cannot call currval until you have called nextval. You have to use SQL to access sequence numbers – even in PL/SQL blocks. If you want to minimise ‘lost’ values, adjust sequence_cache_entries. You need to consider the rate at which you use the sequence and pick a suitable value for the CACHE size. Do not use the ORDER option on Parallel Server systems unless you are confident that you can put up with the side-effects. Jonathan Lewis is a freelance consultant with 14 years experience of Oracle. He specialises in short-term contracts advising on strategic use of Oracle, physical database design, trouble-shooting and training. In his spare time, he is the chairman of the UNIX SIG of the UKOUG. This article, and many others describing best practices with Oracle, can be found on his web-site www.jlcomp.demon.co.uk