Disk Storage The collection of information that makes up files must be physically stored on some storage medium. The information can then be retrieved, updated and processes as needed. There are two main categories of computer storage media. These are: Primary Storage – storage media that can be directly accessed by the central processing unit (CPU) such as main memory (RAM) and cache memory. Primary storage provides fast access but is limited, due to the expense. Secondary Storage – there are various storage devices included in this category, such as magnetic disks, optical disks, and tapes. These devices have a larger storage capacity than primary storage, but data residing on secondary storage devices cannot be processed directly by the CPU. Before processing data in secondary storage, it must first be transferred to primary storage. The memory hierarchy refers to the hierarchy of storage media where data resides. The highest speed memory is the most expensive, and is available with the least capacity, the lowest speed memory is the least expensive and has an almost limitless capacity. The primary storage level includes cache memory, which is used by the CPU to speed up execution of programs, Dynamic RAM (DRAM), also referred to as main memory, which keeps programs and data being used by the CPU. The secondary storage level includes magnetic disks, CDs, and tapes. The storage capacity is measured in kilobytes, megabytes, gigabytes and terabytes. Programs reside and execute in DRAM. Large databases reside in secondary storage and required portions of the databases are read into and written from buffers in main memory as needed. There are cases where entire databases are kept in main memory, with a backup copy on disk, these are called main memory databases. See the course textbook, Chapter 13 for more examples of secondary memory devices. Storing Databases Databases store large amounts of information that must be available for long periods of time. During this period, the data is continually accesses and processed. Databases are generally stored permanently on magnetic disk secondary storage devices. This is because databases are too large to fit entirely in main memory, secondary storage is non-volatile, meaning that permanent loss of data is less common than volatile storage such as main memory, and because the cost of storage is less for magnetic disks than for primary storage. Most database applications will only need a small portion of the database at once for processing. When a portion of the database is needed, it is first located on the disk, copied to main memory for processing, then rewritten to the disk if changes are made. There are a number of different primary file organizations. These file organizations determine how the records of a file are physically written to the disk, and how they can be accessed. A heap file (unordered file), places records on disk by appending new records to the end of the file, and a sorted file (sequential file) keeps the records ordered by the value of a particular field, called a sort key. We will go into more detail later in Chapter 13. Magnetic Disk Devices The most basic unit of data stored on a magnetic disk is a bit. By magnetizing an area on a disk in a certain way, it can be made to represent a bit value of either 0 or 1. These bits are grouped into bytes. Bytes are typically 4 to 8 bits, depending on the device. Commonly it is assumed that one byte represents a single character of information. The capacity of a disk is measured by the number of bytes it can store. Disk capacities can be measured into the terabytes, and continue to grow as technology improves. Disk Structures Disks are made of magnetic material shaped as a thin, circular disk. Disks can be single-sided, meaning they only store data on one surface, or double-sided, meaning data can be stored on both surfaces. Disks are assembled into disk packs, which includes many disks and many surfaces. Information is stored on disks in concentric circles, having small width. Each circle is called a track. In disk packs, the tracks with the same diameter on the multiple surfaces are called a cylinder, because of the shape it would form if connected in space. track spindle read/write head disk rotation cylinder of tracks actuator track – concentric circles on a disk surface which store information. cylinder – tracks with the same diameter on various surfaces of a disk pack. read/write head – reads or writes a block of data. For a disk pack, there is one read/write head for each disk surface. actuator – moves the read/write heads in unison and positions them over the cylinder of tracks specified in a block address. The number of tracks on a disk ranges from a few hundred to a few thousand. Each track has a capacity ranging from 10 Kbytes to 150 Kbytes. Tracks are divided into smaller blocks or sectors. sector sector – arc of a track, which divides a track into smaller blocks. The division of a track into disk blocks is set by the operating system during formatting. The block size is fixed, and cannot be changed dynamically. Blocks are separated by interblock gaps, which include specially coded control information written during formatting. The information is used to determine which block follows each interblock gap. The transfer of data between main memory and the disk takes place in units of disk blocks. The hardware address of a block (combination of a cylinder number, track number, and the block number) is supplied to the disk I/O hardware. The address of a buffer is also provided. A buffer is a reserved area in main storage that holds one block. For a read command, the block from the disk is copied into the buffer, for a write command, the contents of the buffer are copied into a disk block. The disk drives for the hard disks rotate continuously at a constant speed. Once all the read/write heads have been moved to a particular cylinder, and is positioned on the right track and the block specified in the block address moves under the read write head, data transfer can begin. Measuring Transfer Time There are 3 main parameters used to measure the time it takes to transfer data from a disk to memory, or from memory to the disk. These are: seek time: the time it takes to move the arm to the correct cylinder. rotational latency: the time it takes for the disk to revolve so the correct place on the track is just under the head. block transfer time: the time it takes to transfer the bytes of data (block) from the disk to the memory, or from the memory to the disk. The bits on the innermost track are stored closer together than the bits on the outermost track, so the number of bits on each track is the same, and the block transfer time for a given block size is the same no matter what track the block is on. Because the diameter of the innermost track are smaller than the outer tracks, there must be a variation in the number of bits per centimetre on the different tracks. The time it takes to locate and transfer an arbitrary block, given its address is the seek time, plus the rotational delay, plus the block transfer time. Time to Locate and Transfer Block = seek_time(s) + rotational_delay(r) + block_transfer_time(btt) The seek time and rotational delay are usually larger than the block transfer time, because mechanical rather than electrical movement is involved. Disks are random access secondary storage devices, because disk blocks can be accessed at random once we know the address. Magnetic tapes are sequential access , because to access the nth block on the tape we must first scan over the first n-1 blocks. For this reason tape access is slow, and is not usually used to store online data, but is used mostly for backing up databases. Sequential Reading of Blocks If the blocks being read are adjacent, the time needed to read n blocks is the time for one average seek, one average rotational latency and b block transfer times. This is because once the first block is accessed, because the blocks are adjacent, the seek time and rotational latency to access the subsequent blocks is 0. Therefore: Sequential time = s + r + (b x btt) Random Reading of Blocks If the blocks being read are placed randomly on the disks, the time to read the blocks is the sum of the average seek time, the rotational latency, and the block transfer time, multiplied by b (the number of blocks to read). Therefore: Random Time = b x (s + r + btt) Example: So let’s see an example. Assume that we want to read 100 blocks, with the parameter values given. s = 16 ms (milliseconds) r = 8.3 ms btt = 0.8 ms b = 100 First assume that the blocks are placed sequentially on the disk drive, therefore the transfer time is: Sequential Transfer Time = s + r + (b x btt) = 16 + 8.3 + (100 x 0.8) = 104.3 ms Now let’s assume the blocks are placed randomly on the disks in the disk drive, meaning that we must make a seek and allow for rotational latency for each block. Random Transfer Time = b x (s + r + btt) = 100 x (16 + 8.3 + 0.8) = 2510 ms From this example we can see that the random time for reading 10 blocks is slower than the sequential time. This shows that it is much faster to read blocks which are stored together than to read blocks that are randomly placed on the disk. Buffering Blocks When blocks are transferred to memory, they are held in buffers. If several blocks need to be transferred from the disk to main memory, several buffers can be reserved in main memory to speed up the transfer. While one buffer is being read/written, the CPU can process data in the other buffer. This is possible because the block transfer is controlled by an independent disk I/O processor, which can transfer data blocks between main memory and the disk, independent of, and in parallel to the CPU processing data already stored in memory. There are two ways this processing can proceed concurrently (at the same time), in and interleaved fashion or in a parallel fashion. The diagram below illustrates the difference. A A B B C D t1 t2 Interleaved concurrency t3 Parallel t4 If there is only a single CPU controls multiple processes, parallel execution is not possible. However processes can still run concurrently, in an interleaved fashion. Buffering is most useful when processes can run concurrently in a parallel fashion, where multiple CPUs exist, or there is a separate I/O disk processor available. Double buffering can occur when the time required to process a disk block in memory is less than the time required to read the next block and fill the buffer. It can also be used to write a continuous stream of blocks from memory to the disk. Double buffering eliminates the seek time and rotational latency for all but the first block transfer. The CPU is only idle for the time it takes to transfer the first block into memory. Disk block: I/O Fill A Fill B process A Disk Block: Processing Fill A Fill B process B process A time Placing Records on Disk As discussed previously, a record is a collection of related data values or items called fields. The fields along with their data types makes up a record type or record format definition. A data type specifies the values a field can take. A file is a sequence of records. Of every record in the file has the same size in bytes, the file is made up of fixed length records. If the records in the file have different sizes, it is made up of variable length records. Variable length records may occur if: (a) one or more fields in the record are variable length, (b) one or more fields may have multiple values for individual records (repeating field), (c) one or more fields in the record are optional, or (d) the file contains records of different record types (mixed file). It is important for a system to be able to identify the starting position of a field relative to the starting position of a record. For example, where within a record does the field “Salary” start. In fixed length record files this is not difficult, however if the records are variable length, there are different formats for storing the records. It is possible to represent variable length records as fixed length records. For example if there is a field that is optional within a record, each record can contain that field, but may have a special null character to indicate there is no value for that field in that particular record. Although this may seem logical, it results in a waste of space when records do not have values for all physical spaces provided. Fixed Length Records For the following example, let’s assume that we have a file with 4 fields, (1) Name, (2) Kimlik No, (3)Salary, (4) Department. With fixed length, this file may look as follows: 1 Name 21 Kimlik No. 34 Salary 40 Department In this example, the beginning of each field is located in exactly the same location relative to the beginning of the record. Name is 20 characters, Kimlik No. is 13 characters, Salary is 6 characters and Department is 10 characters. Therefore the fixed record length is 49 bytes. Variable Length Records Now let’s assume that the fields in the file vary as follows: (1) Name (variable length), (2) Kimlik No, (3)Salary(optional), (4) Department (optional). Strategies for storing variable length records: a) If each record has a value for every field, but some fields have variable lengths, a separator character (eg. %, ?, $), which will not appear in any field value, can be used to terminate the variable length field. Zafer, Can 123456789 55,000 Payroll Separator b) If some fields are optional, the strategy used depends on whether the total number of fields for the record type is large, but the fields that actually appear in a typical record is small. If this is true, then each record can include a sequence of field names and field value pairs rather than just the field values. In this case, three types of separators can be used, one to separate the field name from the field value, one to indicate the end of a field, and one to indicate the end of the record. Name= Zafer, Can Kimlik=123456789 Record Separator Salary=55,000 Department=Payroll Field Separator = Field Name/Value Separator Optional fields can also be stored using a field type code, and using a combination of field type code/field value pairs. c) If there are repeating fields in the record, one separator is required to separate the repeating values of the field, and another to indicate the end of a field. For a file that includes records of different types, each record is preceded by a record type indicator. Record Blocking As we have seen previously, when operations need to be performed on records, the block containing the record is the unit of data transferred between the main memory and the disk. Because the block is the unit of data transferred, records must be allocated to disk blocks. When the block size is larger than the record size, the block will contain multiple records. We will use the following parameters in this discussion: B: Block size R: Fixed length record size in bytes. If B (size of the block) is greater that R (size of the fixed length record) then we know that we can fit B/R records per block. This is called the blocking factor (bfr). Blocking factor: bfr = B/R In the equation, you will notice that the expression B/R is surrounded by the floor function symbols, . The floor function rounds down the result, x, to an integer. For example if we had the expression, 10/3, without the floor function, the result is 3.33333, but using the floor function, the result would be 3. It is important to note that the floor function always rounds down. So even though in the expression, 11/3, 11/3 is equal to 3.666667, using the floor function the result is 3. If the record size does not divide the block size exactly, the remainder is the unused space in the block. To calculate unused space in the block, the formula is as follows: Unused Block Space = B – (bfr * R) bytes Which is the total block size B, minus the record size multiplied by the number of records in a block. For example, if the block size is 512 bytes, and the record size is 50 bytes, what is the unused block space? B = 512 bytes R = 50 bytes First we need to calculate the blocking factor: bfr = B/R = 512/50 = 10 bfr = 10 Then we can calculate the unused space: Unused space = 512 – (10 * 50) = 12 bytes This unused space can be used by storing part of the record in one block, and the remaining portion that does not fit in the next block. This is called record spanning. Records can be spanned or unspanned. In record spanning, a pointer at the end of the first block points to the block containing the remainder of the record. In cases where a record is larger than a single block, record spanning is necessary. Unspanned organisation is used in cases where B > R, and the records are fixed length, because the record start and end locations are known, and it simplifies the processing. For variable length records, either spanned or unspanned organisation can be used. In order to calculate the number of blocks required, b, for a file containing r records we can use the following formula: b = r / bfr blocks This formula uses the ceiling function, which is the opposite of the floor function. The ceiling function rounds up to the nearest integer, x. For example the function 10/3 , without using the ceiling is 3.33333, using the ceiling function the result is 4. The ceiling function is used because if we are trying to determine the number of blocks required, and the number of records, r, is 10 and the number of records per block (bfr) is 3, 4 blocks are required to store the records, 3 blocks would not be enough to store the records, and partial blocks cannot be used. Example Questions: 13.23 13.24 13.25 13.26 13.38 Allocating File Blocks on Disk There are a number of standard techniques for allocating blocks of a file on disk, these are: a) Contiguous allocation – file blocks are allocated to consecutive dick blocks. Makes reading the file fast using double buffering, but expanding the file difficult. b) Linked allocation – each file block contains a pointer to the next file block. Makes expansion of the file easy, but slow to read the whole file. c) Clusters – a combination of the previous two. Allocates clusters of consecutive disk blocks and then links the clusters. Clusters are sometimes called extents or file segments. d) Indexed allocation – One or more index blocks contain pointers to the actual file blocks. File Headers File Header – contains information about a file needed by system programs to access file records. Includes information on: - Disk addresses of file blocks - Record format descriptions (may include field lengths, field order, field type codes, separator characters and record type codes for variable length records) To search for a record on a disk, one or more blocks are copied into the memory buffers. The application programs then search for the desired record using information contained within the file header. If the address of a block is not known, the application program must do a linear search through the file blocks. Each block is loaded into memory until the record is found, or all blocks have been searched. File Operations Two categories of operations: Retrieval Operations: Do not change any data in the file but locate certain records so their field values can be examined and processed. Update Operations: Change the file by insertion or deletion of records, or modification of fields within a record. For either operation, you must specify a select condition, to identify the record to be processed. The select condition specifies the criteria the desired record(s) must satisfy. When several records satisfy a search condition, the first record (with respect to the physical sequence of file records) is located and designated the current record. The actual operations for locating and accessing file records vary from system to system. A file organisation refers to the organisation of the data of a file into records, blocks and acces structures. This includes the way records and blocks are placed on the storage medium and linked. Access methods are groups of operations that can be applied to a file. In general, several access methods can be applied to a file organisation. File Organisations An important factor in deciding the primary file organisation is how the information will be accessed. Primary file organisations are important to ensure the most efficient access of files and records. Files of Unordered Records (Heap Files) The most basic type of organisation is when records are placed in the file in the order in which they are inserted, so new records are placed at the end of the file. This is called a heap file, or a pile file. Inserting Records With this organisation, inserting a new record into the file is very efficient. The last disk block of the file is copied into a buffer, the new record is added, and the block is rewritten to disk. The address of the last file block is kept in the file header. Searching Records However, searching for a record using any search condition requires a linear search through the file block by block, which is an procedure, meaning it requires a lot of time and system resources. With a file of b blocks, if only one record meets the search criteria, on average, a program will need to read into memory and search half the file blocks before finding the correct record. If no records exist that meet the search criteria, b blocks will have to be searched. Therefore, Number of searches required to locate a record in a file with b blocks = b/2. Deleting Records To delete a record, the program must 1) find the block containing the record, 2) copy the block to the buffer, 3) delete the record from the buffer, and 4) rewrite the modified buffer to disk. As a result of this, unused space is left in the disk block. Deleting a large number of records results in wasted storage space. Another method for deleting records is to have an extra byte stored with the record called a deletion marker. To delete a record, the deletion marker is set to a specific value. Searching algorithms will only consider records that are valid (deletion marker not set to deleted value). With both of these strategies, reorganisation is needed periodically to clean up unused space. An unordered file can have either spanned, or unspanned organization, and may either have fixed length or variable length records. If the records are variable length, modifying a record may require deleting the old record and adding a new record, because the modified record may not fit in the space occupied by the old record. For a file with the following characteristics: 1) unordered, 2) fixed length records, 3) unspanned, and 4) contiguous allocation, accessing records is straightforward. The file can be accessed by its position in the file. For example, assume the file records are numbered 0, 1, 2, 3….r-1, and the records in each block are numbered 0, 1, 2, 3…..bfr – 1, where bfr is the blocking factor, then the ith record in the file is located in the block i/bfr and it is the (i mod bfr)th record in the block. See the image below. position in file block 0 block 1 block 2 . . . position in block 0 0 1 1 2 2 3 3 4 0 5 1 6 2 7 3 8 0 9 1 10 2 From the figure above we can see bfr = 4. If we want to locate the 10th record of the file, it is located in block 10/4 = block 2. The location of the file within block 2 is at the (I mod bfr)th position, which is the (10 mod 4) = 2nd position. These files are often called relative or direct files because records can be easily accessed directly by their positions. Files of Ordered Records (Sorted Files) A sorted file is one that is physically ordered based on the values of one of their fields. The field used to order the file is called the ordering field. If the ordering field is also a key field, then the field is called the ordering key for the file. Advantages of ordered records over unordered files: - Reading records in order of ordering key is efficient because no sorting is required. - Finding the next record from current record, in order of the key requires no additional block accesses because it will be found in the same block as the current record (unless the current record is the last in the block) - Using a search condition based on the value of an ordering key field results in faster access because the binary search technique can be used. A binary search usually accesses log2(b) blocks, whether the record is found or not, whereas a linear search on average accesses (b/2) blocks if the record is found, and b blocks if the record is not found. As you can see the binary search is much faster. Searching Records Search criteria involving the conditions (>, <, >=, <=) on the ordering field are efficient because all records satisfying the condition are contiguous in the file. However ordering is not an advantage for random access, or ordered access of records based on values other than the ordering field. For example, if a file is ordered by Last_Name, and I wish to find all records whose data of birth is after August 1, 1980, the ordering would not provide any advantage over non-ordered files. Inserting and Deleting Records With an ordered file, inserting and deleting are expensive operations because the file must remain physically ordered. To insert a record, we must find the correct position in the file, based on the ordering key, then make space to insert the record into that position. For large files this can be very time consuming, because on average half the records must be moved to make space for the new record. This means that half the file blocks must be read and rewritten after records are moved among them. To make inserting records more efficient, one method that is used is to keep some unused space in each block for new records, however once full, it is back to the original problem. Another option is to create a temporary unordered file called a transaction or overflow file. The ordered file is called the main or master file. New records are inserted at the end of the overflow file rather than the main file. Periodically the overflow file is sorted and merged with the main file. Although insertion becomes very efficient, searching becomes more complex. The master file must be searched using binary search, and if the records are not found, the overflow file must be searched using a linear search. For deleting records, if deletion markers are used, the problem is less severe, and periodic reorganisation is used. Hashing Techniques Another type of file organisation is based on hashing, which provides fast access to records using certain search conditions. This organisation is called a hash file. In most cases the hash field is a key field of the file, called the hash key. Hashing uses a hash function, h, that is applied to the hash field value of a record and gives the address of the disk block in which the record is stored. (ie. to determine where to store a record, apply the hash function to the key). A search for a record within the block can be done in the main memory buffer. To access most records, only a single block transfer is needed. Internal Hashing For internal files, hashing is implemented as a hash table, using an array of records. Imagine that the array index ranges from 0 to M-1. There are M slots whose addresses correspond to the array indexes. A hash function is chosen to transform the hash field value into an integer between 0 and M-1. A common hash function is h(K) = K mod M, which returns the remainder of an integer hash field value K after dividing by M. The resulting value is then used for the record address. If the hash field is non integer, it can be transformed to integers before the mod function is applied using the ASCII value of the character. Other Hashing Functions - Folding involves applying an arithmetic function such as addition or logical function such as exclusive or (Xor) to different portions of the hash field value to calculate the hash address. - Another technique involves picking some digits of the hash field value (ie. Third, fifth, eighth digits) to form the hash address. The difficulty with most hashing functions is that they don’t guarantee that distinct values will hash to distinct addresses. (ie. Many values can hash to the same address). This is because the hash field space (number of possible values that a hash field can take) is much larger than the address space (the number of available addresses for records). The hashing function maps the hash field space to the address space. When the hash field value of a record being inserted hashes to an address space already containing a different record, a collision occurs. There must be some strategy in place for resolving this collision. There are a number of collision resolution mechanisms, including the following: Open Addressing/Linear Probing – if the hash address is occupied, the program checks the next positions in order until an empty address is found. Chaining – Overflow locations are used, by extending the array with overflow positions. Each record location has a pointer field, and if the hash address is occupied, the collision is resolved by placing the new record in an unused overflow location, and setting the pointer of the occupied hash address location to the address of the overflow location. A linked list of the overflow records for each hash address is maintained. Multiple hashing – If the first hash function results in a collision, a second hash function is applied. If there is a collision after the second hash function, either a third hash function is applied, or open addressing is used. Hashing Example with Open Addressing Hash Function = K mod M, where K is the field value, and M is the size of the address space. This will result in the range of values of the hash function to match the address spaces. M=9 h(k) = K mod M K 30 45 24 25 36 54 h(K) 3 0 6 7 0 0 Resulting Array: M 0 1 2 3 4 5 6 7 8 K 45 36 54 30 24 25 Primary Clustering In this example, as we can see, any K value which is a multiple of 9 hashes to the value 0, and produces a collision. Using open addressing, when an address is occupied, the program checks the next position, until it finds an unoccupied position. Because of a collision with any multiple of 9, the values tend to cluster around the collision value. This is referred to as a primary cluster. Average Probes K h(K) # Probes M K 45 36 54 30 63 24 25 0 0 0 3 0 6 7 1 2 3 1 5 1 1 0 1 2 3 4 5 6 7 8 45 36 54 30 63 24 25 To determine the average number of probes, count each time a position is probed. If a value hashes to an empty position the number of probes is 1, if the position is full, the number of probes is 1 plus each additional probe used to find a vacant space. Once you know the number of probes, sum the total probes, and divide by the number of K values, which will give you the average probes. Average Probes = Total Probes / Total Inserts Therefore, in the example above, the average probes is: 14/7 = 2 probes. This value means that there are two searches on average to find the next available position. Hashing with Chaining K 45 47 36 5 14 23 M 0 1 2 3 4 5 6 7 8 h(K) 0 2 0 5 5 5 K 45 47 36 - - 5 14 23 - - Hashing with Overflow Area 0 1 2 3 4 45 Overflow Pointer 9 47 Original Table 5 6 7 8 9 10 11 12 13 5 10 36 14 23 11 Overflow Area Each collision resolution mechanism requires its own algorithms for insertion, retrieval and deletion of records. The algorithms for chaining are the simplest. Deletion algorithms for open addressing are more complex. The ultimate goal of a good hashing function is to distribute records uniformly over the address space so as to minimise collisions, while not leaving many unused locations. Load Factor The load factor, represented by , measures how full the table is. The formula for calculating the load factor is: = # of entries/Total Entries (M) To minimise the number of collisions, the goal value for the load factor is 70%. Example Questions: 13.27 External Hashing for Disk Files - Hashing for disk files is called external hashing. The target address space is made up of buckets (blocks). Each bucket holds multiple records. A bucket can be either one disk block, or multiple contiguous blocks. - The hashing function maps a key into a relative bucket number, rather than assign an absolute block address to the bucket. A table maintained in the file header converts the bucket number into the corresponding block address. See Figure below for example. bucket # Block address on disk 0 1 2 = bucket . . . M-2 M-1 … Because many records will fit into a bucket, the collision problem is less severe, because many records can hash to the same bucket. In the case of a bucket becoming full and new records hashing to a full bucket, collision resolving mechanisms again must be used. A variation of the chaining mechanism can be used, where every bucket maintains a pointer to a linked list of overflow records for that bucket. The pointers are record pointers which include the block address and the relative position within the block. This hashing scheme is called static hashing. This is because a fixed number of buckets, M, is allocated for the address space. If m is the maximum number of records that can fit into a bucket, the total capacity allocated for all buckets is (M * m). If there are significantly fewer than (M * m) records, then there is a lot of unused record space. Conversely, if there are more than (M*m) records numerous collisions will result, and performance will be affected because of the long lists of overflow records. - Searching for a record when using external hashing is expensive if the search field is not the hash field. - Record deletion can be implemented by removing the record from its bucket. If the bucket has an overflow chain, the overflow records can be moved into the bucket to replace the deleted record. Dynamic File Expansion As seen before, a major drawback of static hashing is that address space is fixed. Newer dynamic file organisations based on hashing allow the number of buckets to vary dynamically with only localised reorganisation. Extendible Hashing One example of dynamic file expansion is called extendible hashing. These hashing schemes are based on binary representation of a number. By applying a hashing function to a non negative integer, you can represent the result as a binary number. This binary number is a string of bits which is called the hash value of a record. Records are distributed among buckets based on the values of the leading bits in their hash values. In extendible hashing, a directory of 2d bucket addresses in maintained, where d is the global depth of the directory. The integer value corresponding to the first d bits of a hash value is used as an index to the array to determine a directory entry, and the address in the directory entry determines the bucket in which the corresponding records are stored. A local depth, d1 stored with each bucket specifies the number of bits on which the bucket contents are based. The value d can be increased or decreased by one at a time, doubling or halving the number of entries in the directory. Doubling is needed if the bucket whose local depth matches the global depth overflows, halving may occur after some deletions (d > d1). Example: 0 0 0 0 0 0 0 0 27 26 25 24 23 22 21 20 128 64 32 16 8 4 2 1 The hashing function is: h(k) = K mod 32, therefore we need a directory with at most 32 elements, (5 bits), 25 = 32. K 2657 3760 4692 4871 5659 1821 1074 7115 1620 2428 3943 4750 6975 4981 9208 h(K) 1 16 20 7 27 29 18 11 20 28 7 14 31 21 24 Binary h(k) 00001 10000 10100 00111 11011 11101 10010 01011 10100 11100 00111 01110 11111 10101 10111 Go to PowerPoint Presentation for the Extendible Hashing Example, using these values. Linear Hashing Linear hashing allows a hash file to expand and shrink its number of buckets dynamically without needing a directory. The file starts with M buckets, numbered 0 to M-1, and uses the initial hash function h(K)= K mod M. Overflow because of collisions is needed, and can be handled using an overflow chain for each bucket. When a collision leads to an overflow record in ANY file bucket, the first bucket is split into two buckets, the original bucket 0, and a new bucket M at the end of the file. The records in bucket 0 are distributed between the two buckets based on a different hashing function, hi+1(K) = K mod M. An important property of the two hash functions is that any records that hashed to bucket 0 based on hi will hash to either bucket 0 or bucket M based on hi+1. As further collisions lead to overflows, additional buckets are split in linear order (first split bucket 0, then bucket 1, then bucket 2, etc). If enough overflow occurs, all the original buckets will have been split, so the file will have 2M buckets instead of M buckets, and all buckets use the hash function, hi+1. The records in the overflow are eventually redistributed to regular buckets using the hash function hi+1 via a delayed split of their buckets. There is no directory, only a value n which is initially set to 0 and is incremented by 1 whenever a split occurs. Class Example: hi(K) = K mod 5 hi+1(K) = K mod 10 Using Linear Hashing, assign the following records to buckets, using the hash functions above. K h(K) 16 1 17 2 30 0 49 4 25 0 21 1 48 3 32 2 43 3 54 4 77 2 79 4 26 1 103 3 Example Questions: 13.28 13.29