article

advertisement
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
Download