Apress.Expert.Oracle.Database.Architecture.9i.and.10g.Programming.Techniques.and.Solutions.Sep.2005
CHAPTER 6 ■ ■ ■ Locking and Latching One of the key challenges in developing multiuser, database-driven applications is to maximize concurrent access and, at the same time, ensure that each user is able to read and modify the data in a consistent fashion. The locking mechanisms that allow this to happen are key features of any database, and Oracle excels in providing them. However, Oracle’s implementation of these features is specific to Oracle—just as SQL Server’s implementation is to SQL Server—and it is up to you, the application developer, to ensure that when your application performs data manipulation, it uses these mechanisms correctly. If you fail to do so, your application will behave in an unexpected way, and inevitably the integrity of your data will be compromised (as was demonstrated in Chapter 1). In this chapter, we’ll take a detailed look at how Oracle locks both data (e.g., rows in tables) and shared data structures (such as those found in the SGA). We’ll investigate the granularity to which Oracle locks data and what that means to you, the developer. When appropriate, I’ll contrast Oracle’s locking scheme with other popular implementations, mostly to dispel the myth that row-level locking adds overhead; it adds overhead only if the implementation adds overhead. In the next chapter, we’ll continue this discussion and investigate Oracle’s multiversioning techniques and how locking strategies interact with them. What Are Locks? Locks are mechanisms used to regulate concurrent access to a shared resource. Note how I used the term “shared resource” and not “database row.” It is true that Oracle locks table data at the row level, but it also uses locks at many other levels to provide concurrent access to various resources. For example, while a stored procedure is executing, the procedure itself is locked in a mode that allows others to execute it, but it will not permit another user to alter it in any way. Locks are used in the database to permit concurrent access to these shared resources, while at the same time providing data integrity and consistency. In a single-user database, locks are not necessary. There is, by definition, only one user modifying the information. However, when multiple users are accessing and modifying data or data structures, it is crucial to have a mechanism in place to prevent concurrent modification of the same piece of information. This is what locking is all about. It is very important to understand that there are as many ways to implement locking in a database as there are databases. Just because you have experience with the locking model of one particular relational database management system (RDBMS) does not mean you know everything about locking. For example, before I got heavily involved with Oracle, I used other databases such as Sybase, Microsoft SQL Server, and Informix. All three of these databases 183
184 CHAPTER 6 ■ LOCKING AND LATCHING provide locking mechanisms for concurrency control, but there are deep and fundamental differences in the way locking is implemented in each one. To demonstrate this, I’ll outline my progression from a SQL Server developer to an Informix user and finally an Oracle developer. This happened many years ago, and the SQL Server fans out there will tell me “But we have row-level locking now!” It is true: SQL Server may now use row-level locking, but the way it is implemented is totally different from the way it is done in Oracle. It is a comparison between apples and oranges, and that is the key point. As a SQL Server programmer, I would hardly ever consider the possibility of multiple users inserting data into a table concurrently. It was something that just didn’t often happen in that database. At that time, SQL Server provided only for page-level locking and, since all the data tended to be inserted into the last page of nonclustered tables, concurrent inserts by two users was simply not going to happen. ■Note A SQL Server clustered table (a table that has a clustered index) is in some regard similar to, but very different from, an Oracle cluster. SQL Server used to only support page (block) level locking, and if every row inserted was to go to the “end” of the table, you would never have had concurrent inserts, concurrent transactions in that database. The clustered index in SQL Server was used to cause rows to be inserted all over the table, in sorted order by the cluster key, and as such was used to improve concurrency in that database. Exactly the same issue affected concurrent updates (since an UPDATE was really a DELETE followed by an INSERT). Perhaps this is why SQL Server, by default, commits or rolls back immediately after execution of each and every statement, compromising transactional integrity in an attempt to gain higher concurrency. So in most cases, with page-level locking, multiple users could not simultaneously modify the same table. Compounding this was the fact that while a table modification was in progress, many queries were also effectively blocked against that table. If I tried to query a table and needed a page that was locked by an update, I waited (and waited and waited). The locking mechanism was so poor that providing support for transactions that took more than a second was deadly—the entire database would appear to “freeze” if you did. I learned a lot of bad habits here. I learned that transactions were “bad” and that you ought to commit rapidly and never hold locks on data. Concurrency came at the expense of consistency. You either wanted to get it right or get it fast. I came to believe that you couldn’t have both. When I moved on to Informix, things were better, but not by much. As long as I remembered to create a table with row-level locking enabled, then I could actually have two people simultaneously insert data into that table. Unfortunately, this concurrency came at a high price. Row-level locks in the Informix implementation were expensive, both in terms of time and memory. It took time to acquire and “unacquire” or release them, and each lock consumed real memory. Also, the total number of locks available to the system had to be computed prior to starting the database. If you exceeded that number, then you were just out of luck. Consequently, most tables were created with page-level locking anyway, and, as with SQL Server, both row and page-level locks would stop a query in its tracks. As a result, I found that once again I would want to commit as fast as I could. The bad habits I picked up using SQL Server
- Page 178 and 179: CHAPTER 4 ■ MEMORY STRUCTURES 133
- Page 180 and 181: CHAPTER 4 ■ MEMORY STRUCTURES 135
- Page 182 and 183: CHAPTER 4 ■ MEMORY STRUCTURES 137
- Page 184 and 185: CHAPTER 4 ■ MEMORY STRUCTURES 139
- Page 186 and 187: CHAPTER 4 ■ MEMORY STRUCTURES 141
- Page 188 and 189: CHAPTER 4 ■ MEMORY STRUCTURES 143
- Page 190 and 191: CHAPTER 4 ■ MEMORY STRUCTURES 145
- Page 192 and 193: CHAPTER 4 ■ MEMORY STRUCTURES 147
- Page 194 and 195: CHAPTER 4 ■ MEMORY STRUCTURES 149
- Page 196 and 197: CHAPTER 4 ■ MEMORY STRUCTURES 151
- Page 198 and 199: CHAPTER 4 ■ MEMORY STRUCTURES 153
- Page 200 and 201: CHAPTER 5 ■ ■ ■ Oracle Proces
- Page 202 and 203: CHAPTER 5 ■ ORACLE PROCESSES 157
- Page 204 and 205: CHAPTER 5 ■ ORACLE PROCESSES 159
- Page 206 and 207: CHAPTER 5 ■ ORACLE PROCESSES 161
- Page 208 and 209: CHAPTER 5 ■ ORACLE PROCESSES 163
- Page 210 and 211: CHAPTER 5 ■ ORACLE PROCESSES 165
- Page 212 and 213: CHAPTER 5 ■ ORACLE PROCESSES 167
- Page 214 and 215: CHAPTER 5 ■ ORACLE PROCESSES 169
- Page 216 and 217: CHAPTER 5 ■ ORACLE PROCESSES 171
- Page 218 and 219: CHAPTER 5 ■ ORACLE PROCESSES 173
- Page 220 and 221: CHAPTER 5 ■ ORACLE PROCESSES 175
- Page 222 and 223: CHAPTER 5 ■ ORACLE PROCESSES 177
- Page 224 and 225: CHAPTER 5 ■ ORACLE PROCESSES 179
- Page 226 and 227: CHAPTER 5 ■ ORACLE PROCESSES 181
- Page 230 and 231: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 232 and 233: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 234 and 235: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 236 and 237: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 238 and 239: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 240 and 241: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 242 and 243: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 244 and 245: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 246 and 247: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 248 and 249: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 250 and 251: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 252 and 253: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 254 and 255: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 256 and 257: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 258 and 259: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 260 and 261: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 262 and 263: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 264 and 265: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 266 and 267: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 268 and 269: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 270 and 271: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 272 and 273: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 274 and 275: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 276 and 277: CHAPTER 7 ■ ■ ■ Concurrency a
184<br />
CHAPTER 6 ■ LOCKING AND LATCHING<br />
provide locking mechanisms for concurrency control, but there are deep <strong>and</strong> fundamental<br />
differences in the way locking is implemented in each one. To demonstrate this, I’ll outline my<br />
progression from a SQL Server developer to an Informix user <strong>and</strong> finally an <strong>Oracle</strong> developer.<br />
This happened many years ago, <strong>and</strong> the SQL Server fans out there will tell me “But we have<br />
row-level locking now!” It is true: SQL Server may now use row-level locking, but the way it is<br />
implemented is totally different from the way it is done in <strong>Oracle</strong>. It is a comparison between<br />
apples <strong>and</strong> oranges, <strong>and</strong> that is the key point.<br />
As a SQL Server programmer, I would hardly ever consider the possibility of multiple<br />
users inserting data into a table concurrently. It was something that just didn’t often happen<br />
in that database. At that time, SQL Server provided only for page-level locking <strong>and</strong>, since all<br />
the data tended to be inserted into the last page of nonclustered tables, concurrent inserts by<br />
two users was simply not going to happen.<br />
■Note A SQL Server clustered table (a table that has a clustered index) is in some regard similar to, but<br />
very different from, an <strong>Oracle</strong> cluster. SQL Server used to only support page (block) level locking, <strong>and</strong> if every<br />
row inserted was to go to the “end” of the table, you would never have had concurrent inserts, concurrent<br />
transactions in that database. The clustered index in SQL Server was used to cause rows to be inserted<br />
all over the table, in sorted order by the cluster key, <strong>and</strong> as such was used to improve concurrency in that<br />
database.<br />
Exactly the same issue affected concurrent updates (since an UPDATE was really a DELETE<br />
followed by an INSERT). Perhaps this is why SQL Server, by default, commits or rolls back<br />
immediately after execution of each <strong>and</strong> every statement, compromising transactional<br />
integrity in an attempt to gain higher concurrency.<br />
So in most cases, with page-level locking, multiple users could not simultaneously<br />
modify the same table. Compounding this was the fact that while a table modification was in<br />
progress, many queries were also effectively blocked against that table. If I tried to query a<br />
table <strong>and</strong> needed a page that was locked by an update, I waited (<strong>and</strong> waited <strong>and</strong> waited). The<br />
locking mechanism was so poor that providing support for transactions that took more than a<br />
second was deadly—the entire database would appear to “freeze” if you did. I learned a lot of<br />
bad habits here. I learned that transactions were “bad” <strong>and</strong> that you ought to commit rapidly<br />
<strong>and</strong> never hold locks on data. Concurrency came at the expense of consistency. You either<br />
wanted to get it right or get it fast. I came to believe that you couldn’t have both.<br />
When I moved on to Informix, things were better, but not by much. As long as I remembered<br />
to create a table with row-level locking enabled, then I could actually have two people<br />
simultaneously insert data into that table. Unfortunately, this concurrency came at a high<br />
price. Row-level locks in the Informix implementation were expensive, both in terms of time<br />
<strong>and</strong> memory. It took time to acquire <strong>and</strong> “unacquire” or release them, <strong>and</strong> each lock consumed<br />
real memory. Also, the total number of locks available to the system had to be computed prior<br />
to starting the database. If you exceeded that number, then you were just out of luck. Consequently,<br />
most tables were created with page-level locking anyway, <strong>and</strong>, as with SQL Server,<br />
both row <strong>and</strong> page-level locks would stop a query in its tracks. As a result, I found that once<br />
again I would want to commit as fast as I could. The bad habits I picked up using SQL Server