mirror of
https://github.com/LCTT/TranslateProject.git
synced 2024-12-26 21:30:55 +08:00
Merge branch 'LCTT:master' into master
This commit is contained in:
commit
b80822dde0
@ -1,108 +0,0 @@
|
||||
[#]: subject: (Tune your MySQL queries like a pro)
|
||||
[#]: via: (https://opensource.com/article/21/5/mysql-query-tuning)
|
||||
[#]: author: (Dave Stokes https://opensource.com/users/davidmstokes)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (unigeorge)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
|
||||
Tune your MySQL queries like a pro
|
||||
======
|
||||
Optimizing your queries isn't a dark art; it's just simple engineering.
|
||||
![woman on laptop sitting at the window][1]
|
||||
|
||||
Many people consider tuning database queries to be some mysterious "dark art" out of a Harry Potter novel; with the wrong incantation, your data turns from a valuable resource into a pile of mush.
|
||||
|
||||
In reality, tuning queries for a relational database system is simple engineering and follows easy-to-understand rules or heuristics. The query optimizer translates the query you send to a [MySQL][2] instance, and then it determines the best way to get the requested data using those heuristics combined with what it knows about your data. Reread the last part of that: _"what it knows about your data_." The less the query optimizer has to guess about where your data is located, the better it can create a plan to deliver your data.
|
||||
|
||||
To give the optimizer better insight about the data, you can use indexes and histograms. Used properly, they can greatly increase the speed of a database query. If you follow the recipe, you will get something you will like. But if you add your own ingredients to that recipe, you may not get what you want.
|
||||
|
||||
### Cost-based optimizer
|
||||
|
||||
Most modern relational databases use a cost-based optimizer to determine how to retrieve your data out of the database. That cost is based on reducing very expensive disk reads as much as possible. The query optimizer code inside the database server keeps statistics on getting that data as it is encountered, and it builds a historical model of what it took to get the data.
|
||||
|
||||
But historical data can be out of date. It's like going to the store to buy your favorite snack and being shocked at a sudden price increase or that the store closed. Your server's optimization process may make a bad assumption based on old information, and that will produce a poor query plan.
|
||||
|
||||
A query's complexity can work against optimization. The optimizer wants to deliver the lowest-cost query of the available options. Joining five different tables means that there are five-factorial or 120 possible combinations about which to join to what. Heuristics are built into the code to try to shortcut evaluating all the possible options. MySQL wants to generate a new query plan every time it sees a query, while other databases such as Oracle can have a query plan locked down. This is why giving detailed information on your data to the optimizer is vital. For consistent performance, it really helps to have up-to-date information for the query optimizer to use when making query plans.
|
||||
|
||||
Also, rules are built into the optimizer with assumptions that probably do not match the reality of your data. The query optimizer will assume all the data in a column is evenly distributed among all the rows unless it has other information. And it will default to the smaller of two possible indexes if it sees no alternative. While the cost-based model for an optimizer can make a lot of good decisions, you can smack into cases where you will not get an optimal query plan.
|
||||
|
||||
### A query plan?
|
||||
|
||||
A query plan is what the optimizer will generate for the server to execute from the query. The way to see the query plan is to prepend the word `EXPLAIN` to your query. For example, the following query asks for the name of a city from the city table and the name of the corresponding country table, and the two tables are linked by the country's unique code. This case is interested only in the top five cities alphabetically from the United Kingdom:
|
||||
|
||||
|
||||
```
|
||||
SELECT city.name AS 'City',
|
||||
country.name AS 'Country'
|
||||
FROM city
|
||||
JOIN country ON (city.countrycode = country.code)
|
||||
WHERE country.code = 'GBR'
|
||||
LIMIT 5;
|
||||
```
|
||||
|
||||
Prepending `EXPLAIN` in front of this query will give the query plan generated by the optimizer. Skipping over all but the end of the output, it is easy to see the optimized query:
|
||||
|
||||
|
||||
```
|
||||
SELECT `world`.`city`.`Name` AS `City`,
|
||||
'United Kingdom' AS `Country`
|
||||
FROM `world`.`city`
|
||||
JOIN `world`.`country`
|
||||
WHERE (`world`.`city`.`CountryCode` = 'GBR')
|
||||
LIMIT 5;
|
||||
```
|
||||
|
||||
The big changes are that `country.name as 'Country'` was changed to `'United Kingdom' AS 'Country'` and the `WHERE` clause went from looking in the country table to the city table. The optimizer determined that these two changes will provide a faster result than the original query.
|
||||
|
||||
### Indexes
|
||||
|
||||
You will hear indexes and keys used interchangeably in the MySQL-verse. However, indexes are made up of keys, and keys are a way to identify a record, hopefully uniquely. If a column is designed as a key, the optimizer can search a list of those keys to find the desired record without having to read the entire table. Without an index, the server has to start at the first row of the first column and read through every row of data. If the column was created as a unique index, then the server can go to that one row of data and ignore the rest. The more unique the value of the index (also known as its cardinality), the better. Remember, we are looking for faster ways of getting to the data.
|
||||
|
||||
The MySQL default InnoDB storage engine wants your table to have a primary key and will store your data in a B+ tree by that key. A recently added MySQL feature is invisible columns—columns that do not return data unless the column is explicitly named in the query. For example, `SELECT * FROM foo;` doesn't provide any columns that are designated as hidden. This feature provides a way to add a primary key to older tables without recoding all the queries to include that new column.
|
||||
|
||||
To make this even more complicated, there are many types of indexes, such as functional, spatial, and composite. There are even cases where you can create an index that will provide all the requested information for a query so that there is no need to access the data table.
|
||||
|
||||
Describing the various indexes is beyond the scope of this article, so just think of an index as a shortcut to the record or records you desire. You can create an index on one or more columns or part of those columns. My physician's system can look up my records by the first three letters of my last name and birthdate. Using multiple columns requires using the most unique field first, then the second most unique, and so forth. An index on year-month-day works for year-month-day, year-month, and year searches, but it doesn't work for day, month-day, or year-day searches. It helps to design your indexes around how you want to use your data.
|
||||
|
||||
### Histograms
|
||||
|
||||
A histogram is a distribution of your data. If you were alphabetizing people by their last name, you could use a "logical bucket" for the folks with last names starting with the letters A to F, then another for G to J, and so forth. The optimizer assumes that the data is evenly distributed within the column, but this is rarely the case in practical use.
|
||||
|
||||
MySQL provides two types of histograms: equal height, where all the data is divided equally among the buckets, and singleton, where a single value is in a bucket. You can have up to 1,024 buckets. The amount of buckets to choose for your data column depends on many factors, including how many distinct values you have, how skewed your data is, and how high your accuracy really needs to be. After a certain amount of buckets, there are diminishing returns.
|
||||
|
||||
This command will create a histogram of 10 buckets on column c1 of table t:
|
||||
|
||||
|
||||
```
|
||||
`ANALYZE TABLE t UPDATE HISTOGRAM ON c1 WITH 10 BUCKETS;`
|
||||
```
|
||||
|
||||
Imagine you sell small, medium, and large socks, and each size has its own bin for storage. To find the size you need, you go to the bin for that size. MySQL has had histograms since MySQL 8.0 was released three years ago, yet they are not as well-known as indexes. Unlike indexes, there is no overhead for inserting, updating, or deleting a record. To update an index, an `ANALYZE TABLE` command must be updated. This is a good approach when the data does not churn very much and frequent changes to the data will reduce the efficiency.
|
||||
|
||||
### Indexes or histograms?
|
||||
|
||||
Use indexes for unique items where you need to access the data directly. There is overhead for updates, deletes, and inserts, but you get speedy access if your data is properly architected. Use histograms for data that does not get updated frequently, such as quarterly results for the last dozen years.
|
||||
|
||||
### Parting thoughts
|
||||
|
||||
This article grew out of a recent presentation at the [Open Source 101 conference][3]. And that presentation grew out of a workshop at a [PHP UK Conference][4]. Query tuning is a complex subject, and each time I present on indexes and histograms, I find ways to refine my presentation. But each presentation also shows that many folks in the software world are not well-versed on indexes and tend to use them incorrectly. Histograms have not been around long enough (I hope) to have been misused similarly.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/5/mysql-query-tuning
|
||||
|
||||
作者:[Dave Stokes][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[unigeorge](https://github.com/unigeorge)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/davidmstokes
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/lenovo-thinkpad-laptop-window-focus.png?itok=g0xPm2kD (young woman working on a laptop)
|
||||
[2]: https://www.mysql.com/
|
||||
[3]: https://opensource101.com/
|
||||
[4]: https://www.phpconference.co.uk/
|
@ -0,0 +1,708 @@
|
||||
[#]: subject: "Code memory safety and efficiency by example"
|
||||
[#]: via: "https://opensource.com/article/21/8/memory-programming-c"
|
||||
[#]: author: "Marty Kalin https://opensource.com/users/mkalindepauledu"
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Code memory safety and efficiency by example
|
||||
======
|
||||
Learn more about memory safety and efficiency
|
||||
![Code going into a computer.][1]
|
||||
|
||||
C is a high-level language with close-to-the-metal features that make it seem, at times, more like a portable assembly language than a sibling of Java or Python. Among these features is memory management, which covers an executing program's safe and efficient use of memory. This article goes into the details of memory safety and efficiency through code examples in C and a code segment from the assembly language that a modern C compiler generates.
|
||||
|
||||
Although the code examples are in C, the guidelines for safe and efficient memory management are the same for C++. The two languages differ in various details (e.g., C++ has object-oriented features and generics that C lacks), but these languages share the very same challenges with respect to memory management.
|
||||
|
||||
### Overview of memory for an executing program
|
||||
|
||||
For an executing program (aka _process_), memory is partitioned into three areas: The **stack**, the **heap**, and the **static area**. Here's an overview of each, with full code examples to follow.
|
||||
|
||||
As a backup for general-purpose CPU registers, the _stack_ provides scratchpad storage for the local variables within a code block, such as a function or a loop body. Arguments passed to a function count as local variables in this context. Consider a short example:
|
||||
|
||||
|
||||
```
|
||||
void some_func(int a, int b) {
|
||||
int n;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Storage for the arguments passed in parameters **a** and **b** and the local variable **n** would come from the stack unless the compiler could find general-purpose registers instead. The compiler favors such registers for scratchpad because CPU access to these registers is fast (one clock tick). However, these registers are few (roughly sixteen) on the standard architectures for desktop, laptop, and handheld machines.
|
||||
|
||||
At the implementation level, which only an assembly-language programmer would see, the stack is organized as a LIFO (Last In, First Out) list with **push** (insert) and **pop** (remove) operations. The **top** pointer can act as a base address for offsets; in this way, stack locations other than **top** become accessible. For example, the expression **top+16** points to a location sixteen bytes above the stack's **top**, and the expression **top-16** points to sixteen bytes below the **top**. Accordingly, stack locations that implement scratchpad storage are accessible through the **top** pointer. On a standard ARM or Intel architecture, the stack grows from high to low memory addresses; hence, to decrement **top** is to grow the stack for a process.
|
||||
|
||||
To use the stack is to use memory effortlessly and efficiently. The compiler, rather than the programmer, writes the code that manages the stack by allocating and deallocating the required scratchpad storage; the programmer declares function arguments and local variables, leaving the implementation to the compiler. Moreover, the very same stack storage can be reused across consecutive function calls and code blocks such as loops. Well-designed modular code makes stack storage the first memory option for scratchpad, with an optimizing compiler using, whenever possible, general-purpose registers instead of the stack.
|
||||
|
||||
The **heap** provides storage allocated explicitly through programmer code, although the syntax for heap allocation differs across languages. In C, a successful call to the library function **malloc** (or variants such as **calloc**) allocates a specified number of bytes. (In languages such as C++ and Java, the **new** operator serves the same purpose.) Programming languages differ dramatically on how heap-allocated storage is deallocated:
|
||||
|
||||
* In languages such as Java, Go, Lisp, and Python, the programmer does not explicitly deallocate dynamically allocated heap storage.
|
||||
|
||||
|
||||
|
||||
For example, this Java statement allocates heap storage for a string and stores the address of this heap storage in the variable **greeting**:
|
||||
|
||||
|
||||
```
|
||||
`String greeting = new String("Hello, world!");`
|
||||
```
|
||||
|
||||
Java has a garbage collector, a runtime utility that automatically deallocates heap storage that is no longer accessible to the process that allocated the storage. Java heap deallocation is thus automatic through a garbage collector. In the example above, the garbage collector would deallocate the heap storage for the string after the variable **greeting** went out of scope.
|
||||
|
||||
* The Rust compiler writes the heap-deallocation code. This is Rust's pioneering effort to automate heap-deallocation without relying on a garbage collector, which entails runtime complexity and overhead. Hats off to the Rust effort!
|
||||
* In C (and C++), heap deallocation is a programmer task. The programmer who allocates heap storage through a call to **malloc** is then responsible for deallocating this same storage with a matching call to the library function **free**. (In C++, the **new** operator allocates heap storage, whereas the **delete** and **delete[]** operators free such storage.) Here's a C example:
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
char* greeting = malloc(14); /* 14 heap bytes */
|
||||
strcpy(greeting, "Hello, world!"); /* copy greeting into bytes */
|
||||
puts(greeting); /* print greeting */
|
||||
free(greeting); /* free malloced bytes */
|
||||
```
|
||||
|
||||
C avoids the cost and complexity of a garbage collector, but only by burdening the programmer with the task of heap deallocation.
|
||||
|
||||
The **static area** of memory provides storage for executable code such as C functions, string literals such as "Hello, world!", and global variables:
|
||||
|
||||
|
||||
```
|
||||
int n; /* global variable */
|
||||
int main() { /* function */
|
||||
char* msg = "No comment"; /* string literal */
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
This area is static in that its size remains fixed from the start until the end of process execution. Because the static area amounts to a fixed-sized memory footprint for a process, the rule of thumb is to keep this area as small as possible by avoiding, for example, global arrays.
|
||||
|
||||
Code examples in the following sections flesh out this overview.
|
||||
|
||||
### Stack storage
|
||||
|
||||
Imagine a program that has various tasks to perform consecutively, including processing numeric data downloaded every few minutes over a network and stored in a local file. The **stack** program below simplifies the processing (odd integer values are made even) to keep the focus on the benefits of stack storage.
|
||||
|
||||
|
||||
```
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define Infile "incoming.dat"
|
||||
#define Outfile "outgoing.dat"
|
||||
#define IntCount 128000 /* 128,000 */
|
||||
|
||||
void other_task1() { /*...*/ }
|
||||
void other_task2() { /*...*/ }
|
||||
|
||||
void process_data(const char* infile,
|
||||
const char* outfile,
|
||||
const unsigned n) {
|
||||
int nums[n];
|
||||
FILE* input = [fopen][2](infile, "r");
|
||||
if (NULL == infile) return;
|
||||
FILE* output = [fopen][2](outfile, "w");
|
||||
if (NULL == output) {
|
||||
[fclose][3](input);
|
||||
return;
|
||||
}
|
||||
|
||||
[fread][4](nums, n, sizeof(int), input); /* read input data */
|
||||
unsigned i;
|
||||
for (i = 0; i < n; i++) {
|
||||
if (1 == (nums[i] & 0x1)) /* odd parity? */
|
||||
nums[i]--; /* make even */
|
||||
}
|
||||
[fclose][3](input); /* close input file */
|
||||
|
||||
[fwrite][5](nums, n, sizeof(int), output);
|
||||
[fclose][3](output);
|
||||
}
|
||||
|
||||
int main() {
|
||||
process_data(Infile, Outfile, IntCount);
|
||||
|
||||
/** now perform other tasks **/
|
||||
other_task1(); /* automatically released stack storage available */
|
||||
other_task2(); /* ditto */
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
The **main** function at the bottom first calls the **process_data** function, which creates a stack-based array of a size given by argument **n** (128,000 in the current example). Accordingly, the array holds 128,000 x **sizeof(int)** bytes, which comes to 512,000 bytes on standard devices because an **int** is four bytes on these devices. Data then are read into the array (using library function **fread**), processed in a loop, and saved to the local file **outgoing.dat** (using library function **fwrite**).
|
||||
|
||||
When the **process_data** function returns to its caller **main**, the roughly 500MB of stack scratchpad for the **process_data** function become available for other functions in the **stack** program to use as scratchpad. In this example, **main** next calls the stub functions **other_task1** and **other_task2**. The three functions are called consecutively from **main**, which means that all three can use the same stack storage for scratchpad. Because the compiler rather than the programmer writes the stack-management code, this approach is both efficient and easy on the programmer.
|
||||
|
||||
In C, any variable defined inside a block (e.g., a function's or a loop's body) has an **auto** storage class by default, which means that the variable is stack-based. The storage class **register** is now outdated because C compilers are aggressive, on their own, in trying to use CPU registers whenever possible. Only a variable defined inside a block may be **register**, which the compiler changes to **auto** if no CPU register is available.Stack-based programming may be the preferred way to go, but this style does have its challenges. The **badStack** program below illustrates.
|
||||
|
||||
|
||||
```
|
||||
#include <stdio.h>
|
||||
|
||||
const int* get_array(const unsigned n) {
|
||||
int arr[n]; /* stack-based array */
|
||||
unsigned i;
|
||||
for (i = 0; i < n; i++) arr[i] = 1 + 1;
|
||||
|
||||
return arr; /** ERROR **/
|
||||
}
|
||||
|
||||
int main() {
|
||||
const unsigned n = 16;
|
||||
const int* ptr = get_array(n);
|
||||
|
||||
unsigned i;
|
||||
for (i = 0; i < n; i++) [printf][6]("%i ", ptr[i]);
|
||||
[puts][7]("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
The flow of control in the **badStack** program is straightforward. Function **main** calls function **get_array** with an argument of 128, which the called function then uses to create a local array of this size. The **get_array** function initializes the array and returns to **main** the array's identifier **arr**, which is a pointer constant that holds the address of the array's first **int** element.
|
||||
|
||||
The local array **arr** is accessible within the **get_array** function, of course, but this array cannot be legitimately accessed once **get_array** returns. Nonetheless, function **main** tries to print the stack-based array by using the stack address **arr**, which function **get_array** returns. Modern compilers warn about the mistake. For example, here's the warning from the GNU compiler:
|
||||
|
||||
|
||||
```
|
||||
badStack.c: In function 'get_array':
|
||||
badStack.c:9:10: warning: function returns address of local variable [-Wreturn-local-addr]
|
||||
8 | return arr; /** ERROR **/
|
||||
```
|
||||
|
||||
The general rule is that stack-based storage should be accessed only within the code block that contains the local variables implemented with stack storage (in this case, the array pointer **arr** and the loop counter **i**). Accordingly, a function should never return a pointer to stack-based storage.
|
||||
|
||||
### Heap storage
|
||||
|
||||
Several code examples highlight the fine points of using heap storage in C. In the first example, heap storage is allocated, used, and then freed in line with best practice. The second example nests heap storage inside other heap storage, which complicates the deallocation operation.
|
||||
|
||||
|
||||
```
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int* get_heap_array(unsigned n) {
|
||||
int* heap_nums = [malloc][8](sizeof(int) * n);
|
||||
|
||||
unsigned i;
|
||||
for (i = 0; i < n; i++)
|
||||
heap_nums[i] = i + 1; /* initialize the array */
|
||||
|
||||
/* stack storage for variables heap_nums and i released
|
||||
automatically when get_num_array returns */
|
||||
return heap_nums; /* return (copy of) the pointer */
|
||||
}
|
||||
|
||||
int main() {
|
||||
unsigned n = 100, i;
|
||||
int* heap_nums = get_heap_array(n); /* save returned address */
|
||||
|
||||
if (NULL == heap_nums) /* malloc failed */
|
||||
[fprintf][9](stderr, "%s\n", "malloc(...) failed...");
|
||||
else {
|
||||
for (i = 0; i < n; i++) [printf][6]("%i\n", heap_nums[i]);
|
||||
[free][10](heap_nums); /* free the heap storage */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
The **heap** program above has two functions: **main** calls **get_heap_array** with an argument (currently 100) that specifies how many **int** elements the array should have. Because the heap allocation could fail, **main** checks whether **get_heap_array** has returned **NULL**, which signals failure. If the allocation succeeds, **main** prints the **int** values in the array—and immediately thereafter deallocates, with a call to library function **free**, the heap-allocated storage. This is best practice.
|
||||
|
||||
The **get_heap_array** function opens with this statement, which merits a closer look:
|
||||
|
||||
|
||||
```
|
||||
`int* heap_nums = malloc(sizeof(int) * n); /* heap allocation */`
|
||||
```
|
||||
|
||||
The **malloc** library function and its variants deal with bytes; hence, the argument to **malloc** is the number of bytes required for **n** elements of type **int**. (The **sizeof(int)** is four bytes on a standard modern device.) The **malloc** function returns either the address of the first among the allocated bytes or, in case of failure, **NULL**.
|
||||
|
||||
In a successful call to **malloc**, the returned address is 64-bits in size on a modern desktop machine. On handhelds and earlier desktop machines, the address might be 32-bits in size or, depending on age, even smaller. The elements in the heap-allocated array are of type **int**, a four-byte signed integer. The address of these heap-allocated **int**s is stored in the local variable **heap_nums**, which is stack-based. Here's a depiction:
|
||||
|
||||
|
||||
```
|
||||
heap-based
|
||||
stack-based /
|
||||
\ +----+----+ +----+
|
||||
heap-nums--->|int1|int2|...|intN|
|
||||
+----+----+ +----+
|
||||
```
|
||||
|
||||
Once the **get_heap_array** function returns, stack storage for pointer variable **heap_nums** is reclaimed automatically—but the heap storage for the dynamic **int** array persists, which is why the **get_heap_array** function returns (a copy of) this address to **main**, which now is responsible, after printing the array's integers, for explicitly deallocating the heap storage with a call to the library function **free**:
|
||||
|
||||
|
||||
```
|
||||
`free(heap_nums); /* free the heap storage */`
|
||||
```
|
||||
|
||||
The **malloc** function does not initialize heap-allocated storage, which therefore contains random values. By contrast, the **calloc **variant initializes the allocated storage to zeros. Both functions return **NULL** to signal failure.
|
||||
|
||||
In the **heap** example, **main** returns immediately after calling **free**, and the executing program terminates, which allows the system to reclaim any allocated heap storage. Nonetheless, the programmer should develop the habit of explicitly freeing heap storage as soon as it is no longer needed.
|
||||
|
||||
### Nested heap allocation
|
||||
|
||||
The next code example is trickier. C has various library functions that return a pointer to heap storage. Here's a familiar scenario:
|
||||
|
||||
1\. The C program invokes a library function that returns a pointer to heap-based storage, typically an aggregate such as an array or a structure:
|
||||
|
||||
|
||||
```
|
||||
`SomeStructure* ptr = lib_function(); /* returns pointer to heap storage */`
|
||||
```
|
||||
|
||||
2\. The program then uses the allocated storage.
|
||||
|
||||
3\. For cleanup, the issue is whether a simple call to **free** will clean up all of the heap-allocated storage that the library function allocates. For example, the **SomeStructure** instance may have fields that, in turn, point to heap-allocated storage. A particularly troublesome case would be a dynamically allocated array of structures, each of which has a field pointing to more dynamically allocated storage.The following code example illustrates the problem and focuses on designing a library that safely provides heap-allocated storage to clients.
|
||||
|
||||
|
||||
```
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
unsigned id;
|
||||
unsigned len;
|
||||
float* heap_nums;
|
||||
} HeapStruct;
|
||||
unsigned structId = 1;
|
||||
|
||||
HeapStruct* get_heap_struct(unsigned n) {
|
||||
/* Try to allocate a HeapStruct. */
|
||||
HeapStruct* heap_struct = [malloc][8](sizeof(HeapStruct));
|
||||
if (NULL == heap_struct) /* failure? */
|
||||
return NULL; /* if so, return NULL */
|
||||
|
||||
/* Try to allocate floating-point aggregate within HeapStruct. */
|
||||
heap_struct->heap_nums = [malloc][8](sizeof(float) * n);
|
||||
if (NULL == heap_struct->heap_nums) { /* failure? */
|
||||
[free][10](heap_struct); /* if so, first free the HeapStruct */
|
||||
return NULL; /* then return NULL */
|
||||
}
|
||||
|
||||
/* Success: set fields */
|
||||
heap_struct->id = structId++;
|
||||
heap_struct->len = n;
|
||||
|
||||
return heap_struct; /* return pointer to allocated HeapStruct */
|
||||
}
|
||||
|
||||
void free_all(HeapStruct* heap_struct) {
|
||||
if (NULL == heap_struct) /* NULL pointer? */
|
||||
return; /* if so, do nothing */
|
||||
|
||||
[free][10](heap_struct->heap_nums); /* first free encapsulated aggregate */
|
||||
[free][10](heap_struct); /* then free containing structure */
|
||||
}
|
||||
|
||||
int main() {
|
||||
const unsigned n = 100;
|
||||
HeapStruct* hs = get_heap_struct(n); /* get structure with N floats */
|
||||
|
||||
/* Do some (meaningless) work for demo. */
|
||||
unsigned i;
|
||||
for (i = 0; i < n; i++) hs->heap_nums[i] = 3.14 + (float) i;
|
||||
for (i = 0; i < n; i += 10) [printf][6]("%12f\n", hs->heap_nums[i]);
|
||||
|
||||
free_all(hs); /* free dynamically allocated storage */
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
The **nestedHeap** example above centers on a structure **HeapStruct** with a pointer field named **heap_nums**:
|
||||
|
||||
|
||||
```
|
||||
typedef struct {
|
||||
unsigned id;
|
||||
unsigned len;
|
||||
float* heap_nums; /** pointer **/
|
||||
} HeapStruct;
|
||||
```
|
||||
|
||||
The function **get_heap_struct** tries to allocate heap storage for a **HeapStruct** instance, which entails allocating heap storage for a specified number of **float** variables to which the field **heap_nums** points. The result of a successful call to **get_heap_struct** can be depicted as follows, with **hs** as the pointer to the heap-allocated structure:
|
||||
|
||||
|
||||
```
|
||||
hs-->HeapStruct instance
|
||||
id
|
||||
len
|
||||
heap_nums-->N contiguous float elements
|
||||
```
|
||||
|
||||
In the **get_heap_struct** function, the first heap allocation is straightforward:
|
||||
|
||||
|
||||
```
|
||||
HeapStruct* heap_struct = [malloc][8](sizeof(HeapStruct));
|
||||
if (NULL == heap_struct) /* failure? */
|
||||
return NULL; /* if so, return NULL */
|
||||
```
|
||||
|
||||
The **sizeof(HeapStruct)** includes the bytes (four on a 32-bit machine, eight on a 64-bit machine) for the **heap_nums** field, which is a pointer to the **float** elements in a dynamically allocated array. At issue, then, is whether the **malloc** delivers the bytes for this structure or **NULL** to signal failure; if **NULL**, the **get_heap_struct** function returns **NULL** to notify the caller that the heap allocation failed.
|
||||
|
||||
The second attempted heap allocation is more complicated because, at this step, heap storage for the **HeapStruct** has been allocated:
|
||||
|
||||
|
||||
```
|
||||
heap_struct->heap_nums = [malloc][8](sizeof(float) * n);
|
||||
if (NULL == heap_struct->heap_nums) { /* failure? */
|
||||
[free][10](heap_struct); /* if so, first free the HeapStruct */
|
||||
return NULL; /* and then return NULL */
|
||||
}
|
||||
```
|
||||
|
||||
The argument **n** sent to the **get_heap_struct** function indicates how many **float** elements should be in the dynamically allocated **heap_nums** array. If the required **float** elements can be allocated, then the function sets the structure's **id** and **len** fields before returning the heap address of the **HeapStruct**. If the attempted allocation fails, however, two steps are necessary to meet best practice:
|
||||
|
||||
1\. The storage for the **HeapStruct** must be freed to avoid memory leakage. Without the dynamic **heap_nums** array, the **HeapStruct** is presumably of no use to the client function that calls **get_heap_struct**; hence, the bytes for the **HeapStruct** instance should be explicitly deallocated so that the system can reclaim these bytes for future heap allocations.
|
||||
|
||||
2\. **NULL** is returned to signal failure.
|
||||
|
||||
If the call to the **get_heap_struct** function succeeds, then freeing the heap storage is also tricky because it involves two **free** operations in the proper order. Accordingly, the program includes a **free_all** function instead of requiring the programmer to figure out the appropriate two-step deallocation. For review, here's the **free_all** function:
|
||||
|
||||
|
||||
```
|
||||
void free_all(HeapStruct* heap_struct) {
|
||||
if (NULL == heap_struct) /* NULL pointer? */
|
||||
return; /* if so, do nothing */
|
||||
|
||||
[free][10](heap_struct->heap_nums); /* first free encapsulated aggregate */
|
||||
[free][10](heap_struct); /* then free containing structure */
|
||||
}
|
||||
```
|
||||
|
||||
After checking that the argument **heap_struct** is not **NULL**, the function first frees the **heap_nums** array, which requires that the **heap_struct** pointer is still valid. It would be an error to release the **heap_struct** first. Once the **heap_nums** have been deallocated, the **heap_struct** can be freed as well. If **heap_struct** were freed, but **heap_nums** were not, then the **float** elements in the array would be leakage: still allocated bytes but with no possibility of access—hence, of deallocation. The leakage would persist until the **nestedHeap** program exited and the system reclaimed the leaked bytes.
|
||||
|
||||
A few cautionary notes on the **free** library function are in order. Recall the sample calls above:
|
||||
|
||||
|
||||
```
|
||||
[free][10](heap_struct->heap_nums); /* first free encapsulated aggregate */
|
||||
[free][10](heap_struct); /* then free containing structure */
|
||||
```
|
||||
|
||||
These calls free the allocated storage—but they do _not_ set their arguments to **NULL**. (The **free** function gets a copy of an address as an argument; hence, changing the copy to **NULL** would leave the original unchanged.) For example, after a successful call to **free**, the pointer **heap_struct** still holds a heap address of some heap-allocated bytes, but using this address now would be an error because the call to **free** gives the system the right to reclaim and then reuse the allocated bytes.
|
||||
|
||||
Calling **free** with a **NULL** argument is pointless but harmless. Calling **free** repeatedly on a non-**NULL** address is an error with indeterminate results:
|
||||
|
||||
|
||||
```
|
||||
[free][10](heap_struct); /* 1st call: ok */
|
||||
[free][10](heap_struct); /* 2nd call: ERROR */
|
||||
```
|
||||
|
||||
### Memory leakage and heap fragmentation
|
||||
|
||||
The phrase "memory leakage" refers to dynamically allocated heap storage that is no longer accessible. Here's a code segment for review:
|
||||
|
||||
|
||||
```
|
||||
float* nums = [malloc][8](sizeof(float) * 10); /* 10 floats */
|
||||
nums[0] = 3.14f; /* and so on */
|
||||
nums = [malloc][8](sizeof(float) * 25); /* 25 new floats */
|
||||
```
|
||||
|
||||
Assume that the first **malloc** succeeds. The second **malloc** resets the **nums** pointer, either to **NULL** (allocation failure) or to the address of the first **float** among newly allocated twenty-five. Heap storage for the initial ten **float** elements remains allocated but is now inaccessible because the **nums** pointer either points elsewhere or is **NULL**. The result is forty bytes (**sizeof(float) * 10**) of leakage.
|
||||
|
||||
Before the second call to **malloc**, the initially allocated storage should be freed:
|
||||
|
||||
|
||||
```
|
||||
float* nums = [malloc][8](sizeof(float) * 10); /* 10 floats */
|
||||
nums[0] = 3.14f; /* and so on */
|
||||
[free][10](nums); /** good **/
|
||||
nums = [malloc][8](sizeof(float) * 25); /* no leakage */
|
||||
```
|
||||
|
||||
Even without leakage, the heap can fragment over time, which then requires system defragmentation. For example, suppose that the two biggest heap chunks are currently of sizes 200MB and 100MB. However, the two chunks are not contiguous, and process **P** needs to allocate 250MB of contiguous heap storage. Before the allocation can be made, the system must _defragment_ the heap to provide 250MB contiguous bytes for **P**. Defragmentation is complicated and, therefore, time-consuming.
|
||||
|
||||
Memory leakage promotes fragmentation by creating allocated but inaccessible heap chunks. Freeing no-longer-needed heap storage is, therefore, one way that a programmer can help to reduce the need for defragmentation.
|
||||
|
||||
### Tools to diagnose memory leakage
|
||||
|
||||
Various tools are available for profiling memory efficiency and safety. My favorite is [valgrind][11]. To illustrate how the tool works for memory leaks, here's the **leaky** program:
|
||||
|
||||
|
||||
```
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int* get_ints(unsigned n) {
|
||||
int* ptr = [malloc][8](n * sizeof(int));
|
||||
if (ptr != NULL) {
|
||||
unsigned i;
|
||||
for (i = 0; i < n; i++) ptr[i] = i + 1;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void print_ints(int* ptr, unsigned n) {
|
||||
unsigned i;
|
||||
for (i = 0; i < n; i++) [printf][6]("%3i\n", ptr[i]);
|
||||
}
|
||||
|
||||
int main() {
|
||||
const unsigned n = 32;
|
||||
int* arr = get_ints(n);
|
||||
if (arr != NULL) print_ints(arr, n);
|
||||
|
||||
/** heap storage not yet freed... **/
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
The function **main** calls **get_ints**, which tries to **malloc** thirty-two 4-byte **int**s from the heap and then initializes the dynamic array if the **malloc** succeeds. On success, the **main** function then calls **print_ints**. There is no call to **free** to match the call to **malloc**; hence, memory leaks.
|
||||
|
||||
With the **valgrind** toolbox installed, the command below checks the **leaky** program for memory leaks (**%** is the command-line prompt):
|
||||
|
||||
|
||||
```
|
||||
`% valgrind --leak-check=full ./leaky`
|
||||
```
|
||||
|
||||
Below is most of the output. The number on the left, 207683, is the process identifier of the executing **leaky** program. The report provides details of where the leak occurs, in this case, from the call to **malloc** within the **get_ints** function that **main** calls.
|
||||
|
||||
|
||||
```
|
||||
==207683== HEAP SUMMARY:
|
||||
==207683== in use at exit: 128 bytes in 1 blocks
|
||||
==207683== total heap usage: 2 allocs, 1 frees, 1,152 bytes allocated
|
||||
==207683==
|
||||
==207683== 128 bytes in 1 blocks are definitely lost in loss record 1 of 1
|
||||
==207683== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
|
||||
==207683== by 0x109186: get_ints (in /home/marty/gc/leaky)
|
||||
==207683== by 0x109236: main (in /home/marty/gc/leaky)
|
||||
==207683==
|
||||
==207683== LEAK SUMMARY:
|
||||
==207683== definitely lost: 128 bytes in 1 blocks
|
||||
==207683== indirectly lost: 0 bytes in 0 blocks
|
||||
==207683== possibly lost: 0 bytes in 0 blocks
|
||||
==207683== still reachable: 0 bytes in 0 blocks
|
||||
==207683== suppressed: 0 bytes in 0 blocks
|
||||
```
|
||||
|
||||
If function **main** is revised to include a call to **free** right after the one to **print_ints**, then **valgrind** gives the **leaky** program a clean bill of health:
|
||||
|
||||
|
||||
```
|
||||
`==218462== All heap blocks were freed -- no leaks are possible`
|
||||
```
|
||||
|
||||
### Static area storage
|
||||
|
||||
In orthodox C, a function must be defined outside all blocks. This rules out having one function defined inside the body of another, a feature that some C compilers support. My examples stick with functions defined outside all blocks. Such a function is either **static** or **extern**, with **extern** as the default.
|
||||
|
||||
C functions and variables with either **static** or **extern** as their storage class reside in what I've been calling the **static area** of memory because this area has a fixed size during program execution. The syntax for these two storage classes is complicated enough to merit a review. After the review, a full code example brings the syntactic details back to life. Functions or variables defined outside all blocks default to **extern**; hence, the storage class **static** must be explicit for both functions and variables:
|
||||
|
||||
|
||||
```
|
||||
/** file1.c: outside all blocks, five definitions **/
|
||||
int foo(int n) { return n * 2; } /* extern by default */
|
||||
static int bar(int n) { return n; } /* static */
|
||||
extern int baz(int n) { return -n; } /* explicitly extern */
|
||||
|
||||
int num1; /* extern */
|
||||
static int num2; /* static */
|
||||
```
|
||||
|
||||
The difference between **extern** and **static** comes down to scope: an **extern** function or variable may be visible across files. By contrast, a **static** function is visible only in the file that contains the function's _definition_, and a **static** variable is visible only in the file (or a block therein) that has the variable's _definition_:
|
||||
|
||||
|
||||
```
|
||||
static int n1; /* scope is the file */
|
||||
void func() {
|
||||
static int n2; /* scope is func's body */
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
If a **static** variable such as **n1** above is defined outside all blocks, the variable's scope is the file in which the variable is defined. Wherever a **static** variable may be defined, storage for the variable is in the static area of memory.
|
||||
|
||||
An **extern** function or variable is defined outside all blocks in a given file, but the function or variable so defined then may be declared in some other file. The typical practice is to _declare_ such a function or variable in a header file, which is included wherever needed. Some short examples clarify these tricky points.
|
||||
|
||||
Suppose that the **extern** function **foo** is _defined_ in **file1.c**, with or without the keyword **extern**:
|
||||
|
||||
|
||||
```
|
||||
/** file1.c **/
|
||||
int foo(int n) { return n * 2; } /* definition has a body {...} */
|
||||
```
|
||||
|
||||
This function must be _declared_ with an explicit **extern** in any other file (or block therein) for the function to be visible. Here's the declaration that makes the **extern** function **foo** visible in file **file2.c**:
|
||||
|
||||
|
||||
```
|
||||
/** file2.c: make function foo visible here **/
|
||||
extern int foo(int); /* declaration (no body) */
|
||||
```
|
||||
|
||||
Recall that a function declaration does not have a body enclosed in curly braces, whereas a function definition does have such a body.
|
||||
|
||||
For review, header files typically contain function and variable declarations. Source-code files that require the declarations then **#include** the relevant header file(s). The **staticProg** program in the next section illustrates this approach.
|
||||
|
||||
The rules get trickier (sorry!) with **extern** variables. Any **extern** object—function or variable—must be _defined_ outside all blocks. Also, a variable defined outside all blocks defaults to **extern**:
|
||||
|
||||
|
||||
```
|
||||
/** outside all blocks **/
|
||||
int n; /* defaults to extern */
|
||||
```
|
||||
|
||||
However, the **extern** can be explicit in the variable's _definition_ only if the variable is initialized explicitly there:
|
||||
|
||||
|
||||
```
|
||||
/** file1.c: outside all blocks **/
|
||||
int n1; /* defaults to extern, initialized by compiler to zero */
|
||||
extern int n2 = -1; /* ok, initialized explicitly */
|
||||
int n3 = 9876; /* ok, extern by default and initialized explicitly */
|
||||
```
|
||||
|
||||
For a variable defined as **extern** in **file1.c** to be visible in another file such as **file2.c**, the variable must be _declared_ as explicitly **extern** in **file2.c** and not initialized, which would turn the declaration into a definition:
|
||||
|
||||
|
||||
```
|
||||
/** file2.c **/
|
||||
extern int n1; /* declaration of n1 defined in file1.c */
|
||||
```
|
||||
|
||||
To avoid confusion with **extern** variables, the rule of thumb is to use **extern** explicitly in a _declaration_ (required) but not in a _definition_ (optional and tricky). For functions, the **extern** is optional in a definition but needed for a declaration. The **staticProg** example in the next section brings these points together in a full program.
|
||||
|
||||
### The staticProg example
|
||||
|
||||
The **staticProg** program consists of three files: two C source files (**static1.c** and **static2.c**) together with a header file (**static.h**) that contains two declarations:
|
||||
|
||||
|
||||
```
|
||||
/** header file static.h **/
|
||||
#define NumCount 100 /* macro */
|
||||
extern int global_nums[NumCount]; /* array declaration */
|
||||
extern void fill_array(); /* function declaration */
|
||||
```
|
||||
|
||||
The **extern** in the two declarations, one for an array and the other for a function, underscores that the objects are _defined_ elsewhere ("externally"): the array **global_nums** is defined in file **static1.c** (without an explicit **extern**) and the function **fill_array** is defined in file **static2.c** (also without an explicit **extern**). Each source file includes the header file **static.h**.The **static1.c** file defines the two arrays that reside in the static area of memory, **global_nums** and **more_nums**. The second array has a **static** storage class, which restricts its scope to the file (**static1.c**) in which the array is defined. As noted, **global_nums** as **extern** can be made visible in multiple files.
|
||||
|
||||
|
||||
```
|
||||
/** static1.c **/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "static.h" /* declarations */
|
||||
|
||||
int global_nums[NumCount]; /* definition: extern (global) aggregate */
|
||||
static int more_nums[NumCount]; /* definition: scope limited to this file */
|
||||
|
||||
int main() {
|
||||
fill_array(); /** defined in file static2.c **/
|
||||
|
||||
unsigned i;
|
||||
for (i = 0; i < NumCount; i++)
|
||||
more_nums[i] = i * -1;
|
||||
|
||||
/* confirm initialization worked */
|
||||
for (i = 0; i < NumCount; i += 10)
|
||||
[printf][6]("%4i\t%4i\n", global_nums[i], more_nums[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
The **static2.c** file below defines the **fill_array** function, which **main** (in the **static1.c** file) invokes; the **fill_array** function populates the **extern** array named **global_nums**, which is defined in file **static1.c**. The sole point of having two files is to underscore that an **extern** variable or function can be visible across files.
|
||||
|
||||
|
||||
```
|
||||
/** static2.c **/
|
||||
#include "static.h" /** declarations **/
|
||||
|
||||
void fill_array() { /** definition **/
|
||||
unsigned i;
|
||||
for (i = 0; i < NumCount; i++) global_nums[i] = i + 2;
|
||||
}
|
||||
```
|
||||
|
||||
The **staticProg** program can be compiled as follows:
|
||||
|
||||
|
||||
```
|
||||
`% gcc -o staticProg static1.c static2.c`
|
||||
```
|
||||
|
||||
### More details from assembly language
|
||||
|
||||
A modern C compiler can handle any mix of C and assembly language. When compiling a C source file, the compiler first translates the C code into assembly language. Here's the command to save the assembly language generated from the **static1.c** file above:
|
||||
|
||||
|
||||
```
|
||||
`% gcc -S static1.c`
|
||||
```
|
||||
|
||||
The resulting file is **static1.s**. Here's a segment from the top, with added line numbers for readability:
|
||||
|
||||
|
||||
```
|
||||
.file "static1.c" ## line 1
|
||||
.text ## line 2
|
||||
.comm global_nums,400,32 ## line 3
|
||||
.local more_nums ## line 4
|
||||
.comm more_nums,400,32 ## line 5
|
||||
.section .rodata ## line 6
|
||||
.LC0: ## line 7
|
||||
.string "%4i\t%4i\n" ## line 8
|
||||
.text ## line 9
|
||||
.globl main ## line 10
|
||||
.type main, @function ## line 11
|
||||
main: ## line 12
|
||||
...
|
||||
```
|
||||
|
||||
The assembly-language directives such as **.file** (line 1) begin with a period. As the name suggests, a directive guides the assembler as it translates assembly language into machine code. The **.rodata** directive (line 6) indicates that read-only objects follow, including the string constant **"%4i\t%4i\n"** (line 8), which function **main** (line 12) uses to format output. The function **main** (line 12), introduced as a label (the colon at the end makes it so), is likewise read-only.
|
||||
|
||||
In assembly language, labels are addresses. The label **main:** (line 12) marks the address at which the code for the **main** function begins, and the label **.LC0**: (line 7) marks the address at which the format string begins.
|
||||
|
||||
The definitions of the **global_nums** (line 3) and **more_nums** (line 4) arrays include two numbers: 400 is the total number of bytes in each array, and 32 is the number of bits in each of the 100 **int** elements per array. (The **.comm** directive in line 5 stands for **common name**, which can be ignored.)
|
||||
|
||||
The array definitions differ in that **more_nums** is marked as **.local** (line 4), which means that its scope is restricted to the containing file **static1.s**. By contrast, the **global_nums** array can be made visible across multiple files, including the translations of the **static1.c** and **static2.c** files.
|
||||
|
||||
Finally, the **.text** directive occurs twice (lines 2 and 9) in the assembly code segment. The term "text" suggests "read-only" but also covers read/write variables such as the elements in the two arrays. Although the assembly language shown is for an Intel architecture, Arm6 assembly would be quite similar. For both architectures, variables in the **.text** area (in this case, elements in the two arrays) are initialized automatically to zeros.
|
||||
|
||||
### Wrapping up
|
||||
|
||||
For memory-efficient and memory-safe programming in C, the guidelines are easy to state but may be hard to follow, especially when calls to poorly designed libraries are in play. The guidelines are:
|
||||
|
||||
* Use stack storage whenever possible, thereby encouraging the compiler to optimize with general-purpose registers for scratchpad. Stack storage represents efficient memory use and promotes clean, modular code. Never return a pointer to stack-based storage.
|
||||
* Use heap storage carefully. The challenge in C (and C++) is to ensure that dynamically allocated storage is deallocated ASAP. Good programming habits and tools (such as **valgrind**) help to meet the challenge. Favor libraries that provide their own deallocation function(s), such as the **free_all** function in the **nestedHeap** code example.
|
||||
* Use static storage judiciously, as this storage impacts the memory footprint of a process from start to finish. In particular, try to avoid **extern** and **static** arrays.
|
||||
|
||||
|
||||
|
||||
The C code examples are available at my website (<https://condor.depaul.edu/mkalin>).
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/8/memory-programming-c
|
||||
|
||||
作者:[Marty Kalin][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/mkalindepauledu
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/code_computer_development_programming.png?itok=4OM29-82 (Code going into a computer.)
|
||||
[2]: http://www.opengroup.org/onlinepubs/009695399/functions/fopen.html
|
||||
[3]: http://www.opengroup.org/onlinepubs/009695399/functions/fclose.html
|
||||
[4]: http://www.opengroup.org/onlinepubs/009695399/functions/fread.html
|
||||
[5]: http://www.opengroup.org/onlinepubs/009695399/functions/fwrite.html
|
||||
[6]: http://www.opengroup.org/onlinepubs/009695399/functions/printf.html
|
||||
[7]: http://www.opengroup.org/onlinepubs/009695399/functions/puts.html
|
||||
[8]: http://www.opengroup.org/onlinepubs/009695399/functions/malloc.html
|
||||
[9]: http://www.opengroup.org/onlinepubs/009695399/functions/fprintf.html
|
||||
[10]: http://www.opengroup.org/onlinepubs/009695399/functions/free.html
|
||||
[11]: https://www.valgrind.org/
|
126
sources/tech/20210813 Install Linux with LVM.md
Normal file
126
sources/tech/20210813 Install Linux with LVM.md
Normal file
@ -0,0 +1,126 @@
|
||||
[#]: subject: "Install Linux with LVM"
|
||||
[#]: via: "https://opensource.com/article/21/8/install-linux-mint-lvm"
|
||||
[#]: author: "Kenneth Aaron https://opensource.com/users/flyingrhino"
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Install Linux with LVM
|
||||
======
|
||||
A tutorial on getting Linux Mint 20.2 working with logical volume
|
||||
manager (LVM).
|
||||
![Linux keys on the keyboard for a desktop computer][1]
|
||||
|
||||
A couple of weeks ago, the good folks at [Linux Mint][2] released version 20.2 of their open source operating system. The installer built into the live ISO is excellent and only requires a few clicks to install the OS. You even have a built-in partitioner if you want to customize your partitions.
|
||||
|
||||
The installer is mainly focused on a simple install—define your partitions and install into them. For those wanting a more flexible setup—[logical volume manager][3] (LVM) is the way to go—you benefit from setting up volume groups and define your logical volumes within them.
|
||||
|
||||
LVM is a hard drive management system that allows you to create storage space across multiple physical drives. In other words, you could "tether" a few small drives together so your OS treats them as if they were one drive. Beyond that, it has the advantages of live resizing, file system snapshots, and much more. This article isn't a tutorial on LVM (the web is full of [good information on that already][4].) Instead, I aim to keep this page on topic and focus solely on getting Linux Mint 20.2 working with LVM.
|
||||
|
||||
As a desktop OS, the installer is kept simple, and installing LM 20.2 on LVM is slightly more involved but not too complicated. If you select LVM in the installer, you get a setup that's been defined by the Linux Mint devs, and you have no control over the individual volumes at the time of install.
|
||||
|
||||
However, there's a solution—right there in the live ISO—and that solution only requires a few commands in the terminal to set up the LVM, after which you resume the regular installer to complete the job.
|
||||
|
||||
I'm using Linux Mint 20.2 with the [XFCE desktop][5] for my install, but the procedure is similar for the other LM desktops.
|
||||
|
||||
### Partitioning the drive
|
||||
|
||||
In the Linux Mint live ISO, you have access to Linux command-line tools through the terminal and GUI tools. If you need to do any partition work, you can use the command-line `fdisk` or `parted` commands, or the GUI application `gparted`. I want to keep these instructions simple enough for anyone to follow, so I'll use GUI tools where possible and command-line tools where necessary.
|
||||
|
||||
Start by creating a couple of partitions for the install.
|
||||
|
||||
Using `gparted` (launched from the menu), complete the following:
|
||||
|
||||
First, create a partition of 512 MB of type **FAT32** (this is used to ensure the system is bootable.) 512 MB is overkill for most, and you can get away with 256 MB or even less, but with today's big disks, allocating even 512 MB is not a significant concern.
|
||||
|
||||
![Creating a boot partition][6]
|
||||
|
||||
CC BY-SA Seth Kenlon
|
||||
|
||||
Next, create a partition of the rest of the disk of type `lvm2 pv` (this is where your LVM will be.)
|
||||
|
||||
![Partition layout][7]
|
||||
|
||||
CC BY-SA Seth Kenlon
|
||||
|
||||
Now open a terminal window, and escalate your privileges to root:
|
||||
|
||||
|
||||
```
|
||||
$ sudo -s
|
||||
# whoami
|
||||
root
|
||||
```
|
||||
|
||||
Next, you must locate the LVM member (the big partition) you created earlier. Use one of the following commands: `lsblk -f` or `pvs` or `pvscan`.
|
||||
|
||||
|
||||
```
|
||||
# pvs
|
||||
PV VG Fmt [...]
|
||||
/dev/sda2 lvm2 [...]
|
||||
```
|
||||
|
||||
In my case, the partition is located at `/dev/sda2`, but you should replace this with whatever you get in your output.
|
||||
|
||||
Now that you know what device designation your partition has, you can create an LVM volume group there:
|
||||
|
||||
|
||||
```
|
||||
`# vgcreate vg /dev/sda2`
|
||||
```
|
||||
|
||||
You can see the details of the volume group you created using `vgs `or `vgscan`.
|
||||
|
||||
Create the logical volumes you want to use during install. I'm keeping it simple by creating one for the root partition (`/`) and one for `swap`, but you can create more as needed (for example, a separate partition for `/home`.)
|
||||
|
||||
|
||||
```
|
||||
# lvcreate -L 80G -n root vg
|
||||
# lvcreate -L 16G -n swap vg
|
||||
```
|
||||
|
||||
The partition sizes in my examples are arbitrary and based on what I have available. Use partition sizes that make sense for your drive.
|
||||
|
||||
You can view the logical volumes with `lvs` or `lvdisplay`.
|
||||
|
||||
That's it for the terminal.
|
||||
|
||||
### Installing Linux
|
||||
|
||||
Now start the installer program from the desktop icon:
|
||||
|
||||
* Once you get to the **Installation type**, select **Something else**.
|
||||
* Edit the 512 Mb partition and change it to `EFI`.
|
||||
* Edit the root LV and change it to `ext4` (or a file system of your choice). Select to mount it as root and select to format it.
|
||||
* Edit the swap partition and set it as `swap`.
|
||||
* Continue the install process normally—Linux Mint installer puts the files in the correct places and creates the mount points for you.
|
||||
|
||||
|
||||
|
||||
That's it—enjoy the power of LVM in your Linux Mint install.
|
||||
|
||||
If ever you need to resize partitions or do any advanced work on the system—you'll be thankful for choosing LVM.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/8/install-linux-mint-lvm
|
||||
|
||||
作者:[Kenneth Aaron][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/flyingrhino
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/linux_keyboard_desktop.png?itok=I2nGw78_ (Linux keys on the keyboard for a desktop computer)
|
||||
[2]: https://linuxmint.com/
|
||||
[3]: https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)
|
||||
[4]: https://opensource.com/business/16/9/linux-users-guide-lvm
|
||||
[5]: https://opensource.com/article/19/12/xfce-linux-desktop
|
||||
[6]: https://opensource.com/sites/default/files/boot-part.png (Creating a boot partition)
|
||||
[7]: https://opensource.com/sites/default/files/part-layout.png (Partition layout)
|
@ -0,0 +1,231 @@
|
||||
[#]: subject: "Parse command options in Java with commons-cli"
|
||||
[#]: via: "https://opensource.com/article/21/8/java-commons-cli"
|
||||
[#]: author: "Seth Kenlon https://opensource.com/users/seth"
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Parse command options in Java with commons-cli
|
||||
======
|
||||
Let your users modify how your Java application runs with command-line
|
||||
options.
|
||||
![Learning and studying technology is the key to success][1]
|
||||
|
||||
When you enter a command into your terminal, whether it's to launch a GUI app or just a terminal app, there are often [options][2] (sometimes called _switches_ or _flags_) you can use to modify how the application runs. This is a standard set by the [POSIX specification][3], so it's useful for a Java programmer to know how to detect and parse options.
|
||||
|
||||
There are several ways to parse options in Java. My favorite is the [Apache Commons CLI][4] library, called **commons-cli** for short.
|
||||
|
||||
### Installing commons-cli
|
||||
|
||||
If you're using a project management system like [Maven][5] and an IDE, you can install the Apache Commons CLI library in your project properties (such as `pom.xml` or a configuration screen in Eclipse or NetBeans).
|
||||
|
||||
If you're managing libraries manually, you can download [the latest release][6] from the Apache website. Several JAR files come bundled together. The only required JAR is the `commons-cli-X.Y.jar` (where X and Y are the latest version numbers.) Add that JAR to your project, either manually or in your IDE, and then you can use it in your code.
|
||||
|
||||
### Importing a library into your Java code
|
||||
|
||||
To use the **commons-cli** library in your code, you must import it. For this simple option parsing example, you can populate a file called `Main.java` with the standard minimal code:
|
||||
|
||||
|
||||
```
|
||||
package com.opensource.myoptparser;
|
||||
|
||||
import org.apache.commons.cli.*;
|
||||
|
||||
public class Main {
|
||||
public static void main([String][7][] args) {
|
||||
// code
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now you're set to parse options in Java.
|
||||
|
||||
### Defining Boolean options in Java
|
||||
|
||||
The first thing you must do to parse options is to define the valid options your application can accept. Use the `Option` (singular) class to create option objects and the `Options` (plural) class to help keep track of all the options you've created in your project.
|
||||
|
||||
First, create a group for your options, and call it `options` according to convention:
|
||||
|
||||
|
||||
```
|
||||
//code
|
||||
Options options = new Options();
|
||||
```
|
||||
|
||||
Next, define your individual options by listing a short option, a long option, a default Boolean value, and a help message. You then set whether the option is required or not, and finally add the option to the `options` object, which contains all of your options. In this example, I create just one option, arbitrarily called `alpha`:
|
||||
|
||||
|
||||
```
|
||||
//define options
|
||||
[Option][8] alpha = new [Option][8]("a", "alpha", false, "Activate feature alpha");
|
||||
options.addOption(alpha);
|
||||
```
|
||||
|
||||
### Defining options with arguments in Java
|
||||
|
||||
Sometimes you want users to provide information other than just **true** or **false** along with an option. You might want to let a user refer to a configuration file, an input file, or any setting like a date or a color. For this, you use the `builder` method, creating attributes for an option based on its short version (for example, `-c` is a short option, `--config` is a long option). Once it's defined, you add the new option to your `options` group:
|
||||
|
||||
|
||||
```
|
||||
[Option][8] config = [Option][8].builder("c").longOpt("config")
|
||||
.argName("config")
|
||||
.hasArg()
|
||||
.required(true)
|
||||
.desc("set config file").build();
|
||||
options.addOption(config);
|
||||
```
|
||||
|
||||
With the `builder` function, you can set the short version, long version, whether it's required (I set this to **true** in this code, so my application can't run unless this option is provided by the user at launch time), the help message, and so on.
|
||||
|
||||
### Parsing options with Java
|
||||
|
||||
With all possible options defined, you can now iterate over arguments provided by the user, checking to see whether any argument matches your approved list of valid short options. To do this, you create an instance of the **CommandLine** itself, which contains all arguments provided by the user (valid options and otherwise.) You also create a **CommandLineParser** object, which I call `parser` in my code, to facilitate interaction over those arguments. Finally, you can create a **HelpFormatter** object (which I call `helper`) to automatically provide helpful messages to the user when either a required option is missing, or the `--help` or `-h` option is used.
|
||||
|
||||
|
||||
```
|
||||
// define parser
|
||||
CommandLine cmd;
|
||||
CommandLineParser parser = new BasicParser();
|
||||
HelpFormatter helper = new HelpFormatter();
|
||||
```
|
||||
|
||||
Finally, add some conditionals to analyze the options provided by the user as command-line input (discovered and stored in the `cmd` variable). This sample application has two different types of options, but in both cases, you can check whether the option exists with the `.hasOption` method plus the short option name. When an option is detected, you can do whatever needs to be done with the data.
|
||||
|
||||
|
||||
```
|
||||
try {
|
||||
cmd = parser.parse(options, args);
|
||||
if(cmd.hasOption("a")) {
|
||||
[System][9].out.println("Alpha activated");
|
||||
}
|
||||
|
||||
if (cmd.hasOption("c")) {
|
||||
[String][7] opt_config = cmd.getOptionValue("config");
|
||||
[System][9].out.println("Config set to " + opt_config);
|
||||
}
|
||||
} catch ([ParseException][10] e) {
|
||||
[System][9].out.println(e.getMessage());
|
||||
helper.printHelp("Usage:", options);
|
||||
[System][9].exit(0);
|
||||
}
|
||||
```
|
||||
|
||||
The act of parsing potentially generates an error because sometimes the required `-c` or `--config` option could be missing. In that event, a help message is printed, and the application is immediately ended. Because of this error (an _exception_ in Java terminology), you must amend the start of the main method to declare a possible exception:
|
||||
|
||||
|
||||
```
|
||||
`public static void main(String[] args) throws ParseException {`
|
||||
```
|
||||
|
||||
The sample application is now complete.
|
||||
|
||||
### Test your code
|
||||
|
||||
You can test the application in your IDE by adjusting the default parameters passed to your code or just build a JAR file and run it from your terminal. The process for this differs depending on your IDE. Refer to your IDE documentation, read my article on how to LINK-TO-ARTICLE[build a JAR], or see Daniel Oh's article on how to do the same with [Maven][11].
|
||||
|
||||
First, confirm the parser exception by omitting the required `-c` or `--config` option:
|
||||
|
||||
|
||||
```
|
||||
$ java -jar dist/myapp.jar
|
||||
Missing required option: c
|
||||
usage: Usage:
|
||||
-a,--alpha Activate feature alpha
|
||||
-c,--config <config> Set config file
|
||||
```
|
||||
|
||||
Try it again with the options provided:
|
||||
|
||||
|
||||
```
|
||||
java -jar dist/myantapp.jar --config foo -a
|
||||
Alpha activated
|
||||
Config set to foo
|
||||
```
|
||||
|
||||
### Option parsing
|
||||
|
||||
Including options for your users is an important feature for any application. Java and the Apache commons make it easy to do.
|
||||
|
||||
Here's the full demonstration code for your reference:
|
||||
|
||||
|
||||
```
|
||||
package com.opensource.myapp;
|
||||
|
||||
import org.apache.commons.cli.*;
|
||||
|
||||
public class Main {
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
* @throws org.apache.commons.cli.ParseException
|
||||
*/
|
||||
public static void main([String][7][] args) throws [ParseException][10] {
|
||||
// define options
|
||||
Options options = new Options();
|
||||
|
||||
[Option][8] alpha = new [Option][8]("a", "alpha", false, "Activate feature alpha");
|
||||
options.addOption(alpha);
|
||||
|
||||
[Option][8] config = [Option][8].builder("c").longOpt("config")
|
||||
.argName("config")
|
||||
.hasArg()
|
||||
.required(true)
|
||||
.desc("Set config file").build();
|
||||
options.addOption(config);
|
||||
|
||||
// define parser
|
||||
CommandLine cmd;
|
||||
CommandLineParser parser = new BasicParser();
|
||||
HelpFormatter helper = new HelpFormatter();
|
||||
|
||||
try {
|
||||
cmd = parser.parse(options, args);
|
||||
if(cmd.hasOption("a")) {
|
||||
[System][9].out.println("Alpha activated");
|
||||
}
|
||||
|
||||
if (cmd.hasOption("c")) {
|
||||
[String][7] opt_config = cmd.getOptionValue("config");
|
||||
[System][9].out.println("Config set to " + opt_config);
|
||||
}
|
||||
} catch ([ParseException][10] e) {
|
||||
[System][9].out.println(e.getMessage());
|
||||
helper.printHelp("Usage:", options);
|
||||
[System][9].exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using Java and options
|
||||
|
||||
Options allow users to modify how commands work. There are many ways to parse options when using Java, and the `commons-cli` is a robust and flexible open source solution. Give it a try in your next Java project.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/8/java-commons-cli
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/studying-books-java-couch-education.png?itok=C9gasCXr (Learning and studying technology is the key to success)
|
||||
[2]: https://opensource.com/article/21/8/linux-terminal#options
|
||||
[3]: https://opensource.com/article/19/7/what-posix-richard-stallman-explains
|
||||
[4]: https://commons.apache.org/proper/commons-cli/usage.html
|
||||
[5]: https://maven.apache.org/
|
||||
[6]: https://commons.apache.org/proper/commons-cli/download_cli.cgi
|
||||
[7]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string
|
||||
[8]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+option
|
||||
[9]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+system
|
||||
[10]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+parseexception
|
||||
[11]: https://developers.redhat.com/blog/2021/04/08/build-even-faster-quarkus-applications-with-fast-jar
|
@ -0,0 +1,304 @@
|
||||
[#]: subject: "Use dnf updateinfo to read update changelogs"
|
||||
[#]: via: "https://fedoramagazine.org/use-dnf-updateinfo-to-read-update-changelogs/"
|
||||
[#]: author: "Mateus Rodrigues Costa https://fedoramagazine.org/author/mateusrodcosta/"
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Use dnf updateinfo to read update changelogs
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
Cover image background excerpted from photo by [Fotis Fotopoulos][2] on [Unsplash][3]
|
||||
|
||||
This article will explore how to check the changelogs for the Fedora Linux operating system using the command line and _dnf updateinfo_. Instead of showing the commands running on a real Fedora Linux install, this article will demo running the dnf commands in [toolbox][4].
|
||||
|
||||
### Introduction
|
||||
|
||||
If you have used any type of computer recently (be it a desktop, laptop or even a smartphone), you most likely have had to deal with software updates. You might have an opinion about them. They might be a “necessary evil”, something that always breaks your setup and makes you waste hours fixing the new problems that appeared, or you might even like them.
|
||||
|
||||
No matter your opinion, there are reasons to update your software: mainly bug fixes, especially security-related bug fixes. After all, you most likely don’t want someone getting your private data by exploiting a bug that happens because of a interaction between the code of your web browser and the code that renders text on your screen.
|
||||
|
||||
If you manage your software updates in a manual or semi-manual fashion (in comparison to letting the operating system auto-update your software), one feature you should be aware of is “changelogs”.
|
||||
|
||||
A changelog is, as the name hints, a big list of changes between two releases of the same software. The changelog content can vary a lot. It may depend on the team, the type of software, its importance, and the number of changes. It can range from a very simple “several small bugs were fixed in this release”-type message, to a list of links to the bugs fixed on a issue tracker with a small description, to a big and detailed list of changes or elaborate blog posts.
|
||||
|
||||
Now, how do you check the changelogs for the updates?
|
||||
|
||||
If you use Fedora Workstation the easy way to see the changelog with a GUI is with Gnome Software. Select the name of the package or name of the software on the updates page and the changelog is displayed. You could also try your favorite GUI package manager, which will most likely show it to you as well. But how does one do the same thing via CLI?
|
||||
|
||||
### How to use dnf updateinfo
|
||||
|
||||
Start by creating a Fedora 34 toolbox called _updateinfo-demo_:
|
||||
|
||||
```
|
||||
toolbox create --distro fedora --release f34 updateinfo-demo
|
||||
```
|
||||
|
||||
Now, enter the toolbox:
|
||||
|
||||
```
|
||||
toolbox enter updateinfo-demo
|
||||
```
|
||||
|
||||
The commands from here on can also be used on a normal Fedora install.
|
||||
|
||||
First, check the updates available:
|
||||
|
||||
```
|
||||
$ dnf check-update
|
||||
audit-libs.x86_64 3.0.3-1.fc34 updates
|
||||
ca-certificates.noarch 2021.2.50-1.0.fc34 updates
|
||||
coreutils.x86_64 8.32-30.fc34 updates
|
||||
coreutils-common.x86_64 8.32-30.fc34 updates
|
||||
curl.x86_64 7.76.1-7.fc34 updates
|
||||
dnf.noarch 4.8.0-1.fc34 updates
|
||||
dnf-data.noarch 4.8.0-1.fc34 updates
|
||||
expat.x86_64 2.4.1-1.fc34 updates
|
||||
file-libs.x86_64 5.39-6.fc34 updates
|
||||
glibc.x86_64 2.33-20.fc34 updates
|
||||
glibc-common.x86_64 2.33-20.fc34 updates
|
||||
glibc-minimal-langpack.x86_64 2.33-20.fc34 updates
|
||||
krb5-libs.x86_64 1.19.1-14.fc34 updates
|
||||
libcomps.x86_64 0.1.17-1.fc34 updates
|
||||
libcurl.x86_64 7.76.1-7.fc34 updates
|
||||
libdnf.x86_64 0.63.1-1.fc34 updates
|
||||
libeconf.x86_64 0.4.0-1.fc34 updates
|
||||
libedit.x86_64 3.1-38.20210714cvs.fc34 updates
|
||||
libgcrypt.x86_64 1.9.3-3.fc34 updates
|
||||
libidn2.x86_64 2.3.2-1.fc34 updates
|
||||
libmodulemd.x86_64 2.13.0-1.fc34 updates
|
||||
librepo.x86_64 1.14.1-1.fc34 updates
|
||||
libsss_idmap.x86_64 2.5.2-1.fc34 updates
|
||||
libsss_nss_idmap.x86_64 2.5.2-1.fc34 updates
|
||||
libuser.x86_64 0.63-4.fc34 updates
|
||||
libxcrypt.x86_64 4.4.23-1.fc34 updates
|
||||
nano.x86_64 5.8-3.fc34 updates
|
||||
nano-default-editor.noarch 5.8-3.fc34 updates
|
||||
nettle.x86_64 3.7.3-1.fc34 updates
|
||||
openldap.x86_64 2.4.57-5.fc34 updates
|
||||
pam.x86_64 1.5.1-6.fc34 updates
|
||||
python-setuptools-wheel.noarch 53.0.0-2.fc34 updates
|
||||
python-unversioned-command.noarch 3.9.6-2.fc34 updates
|
||||
python3.x86_64 3.9.6-2.fc34 updates
|
||||
python3-dnf.noarch 4.8.0-1.fc34 updates
|
||||
python3-hawkey.x86_64 0.63.1-1.fc34 updates
|
||||
python3-libcomps.x86_64 0.1.17-1.fc34 updates
|
||||
python3-libdnf.x86_64 0.63.1-1.fc34 updates
|
||||
python3-libs.x86_64 3.9.6-2.fc34 updates
|
||||
python3-setuptools.noarch 53.0.0-2.fc34 updates
|
||||
sssd-client.x86_64 2.5.2-1.fc34 updates
|
||||
systemd.x86_64 248.6-1.fc34 updates
|
||||
systemd-libs.x86_64 248.6-1.fc34 updates
|
||||
systemd-networkd.x86_64 248.6-1.fc34 updates
|
||||
systemd-pam.x86_64 248.6-1.fc34 updates
|
||||
systemd-rpm-macros.noarch 248.6-1.fc34 updates
|
||||
vim-minimal.x86_64 2:8.2.3182-1.fc34 updates
|
||||
xkeyboard-config.noarch 2.33-1.fc34 updates
|
||||
yum.noarch 4.8.0-1.fc34 updates
|
||||
```
|
||||
|
||||
OK, so run your first _dnf updateinfo_ command:
|
||||
|
||||
```
|
||||
$ dnf updateinfo
|
||||
Updates Information Summary: available
|
||||
5 Security notice(s)
|
||||
4 Moderate Security notice(s)
|
||||
1 Low Security notice(s)
|
||||
11 Bugfix notice(s)
|
||||
8 Enhancement notice(s)
|
||||
3 other notice(s)
|
||||
```
|
||||
|
||||
This is the summary of updates. As you can see there are security updates, bugfix updates, enhancement updates and some which are not specified.
|
||||
|
||||
Look at the list of updates and which types they belong to:
|
||||
|
||||
```
|
||||
$ dnf updateinfo list
|
||||
FEDORA-2021-e4866762d8 enhancement audit-libs-3.0.3-1.fc34.x86_64
|
||||
FEDORA-2021-1f32e18471 bugfix ca-certificates-2021.2.50-1.0.fc34.noarch
|
||||
FEDORA-2021-b09e010a46 bugfix coreutils-8.32-30.fc34.x86_64
|
||||
FEDORA-2021-b09e010a46 bugfix coreutils-common-8.32-30.fc34.x86_64
|
||||
FEDORA-2021-83fdddca0f Moderate/Sec. curl-7.76.1-7.fc34.x86_64
|
||||
FEDORA-2021-3b74285c43 bugfix dnf-4.8.0-1.fc34.noarch
|
||||
FEDORA-2021-3b74285c43 bugfix dnf-data-4.8.0-1.fc34.noarch
|
||||
FEDORA-2021-523ee0a81e enhancement expat-2.4.1-1.fc34.x86_64
|
||||
FEDORA-2021-07625b9c81 unknown file-libs-5.39-6.fc34.x86_64
|
||||
FEDORA-2021-e14e86e40e Moderate/Sec. glibc-2.33-20.fc34.x86_64
|
||||
FEDORA-2021-e14e86e40e Moderate/Sec. glibc-common-2.33-20.fc34.x86_64
|
||||
FEDORA-2021-e14e86e40e Moderate/Sec. glibc-minimal-langpack-2.33-20.fc34.x86_64
|
||||
FEDORA-2021-8b25e4642f Low/Sec. krb5-libs-1.19.1-14.fc34.x86_64
|
||||
FEDORA-2021-3b74285c43 bugfix libcomps-0.1.17-1.fc34.x86_64
|
||||
FEDORA-2021-83fdddca0f Moderate/Sec. libcurl-7.76.1-7.fc34.x86_64
|
||||
FEDORA-2021-3b74285c43 bugfix libdnf-0.63.1-1.fc34.x86_64
|
||||
FEDORA-2021-ca22b882a5 enhancement libeconf-0.4.0-1.fc34.x86_64
|
||||
FEDORA-2021-f9c139edd8 bugfix libedit-3.1-38.20210714cvs.fc34.x86_64
|
||||
FEDORA-2021-31fdc84207 Moderate/Sec. libgcrypt-1.9.3-3.fc34.x86_64
|
||||
FEDORA-2021-bc56cf7c1f enhancement libidn2-2.3.2-1.fc34.x86_64
|
||||
FEDORA-2021-da2ec14d7f bugfix libmodulemd-2.13.0-1.fc34.x86_64
|
||||
FEDORA-2021-3b74285c43 bugfix librepo-1.14.1-1.fc34.x86_64
|
||||
FEDORA-2021-1db6330a22 unknown libsss_idmap-2.5.2-1.fc34.x86_64
|
||||
FEDORA-2021-1db6330a22 unknown libsss_nss_idmap-2.5.2-1.fc34.x86_64
|
||||
FEDORA-2021-8226c82fe9 bugfix libuser-0.63-4.fc34.x86_64
|
||||
FEDORA-2021-e6916d6758 bugfix libxcrypt-4.4.22-2.fc34.x86_64
|
||||
FEDORA-2021-fed4036fd9 bugfix libxcrypt-4.4.23-1.fc34.x86_64
|
||||
FEDORA-2021-3122d2b8d2 unknown nano-5.8-3.fc34.x86_64
|
||||
FEDORA-2021-3122d2b8d2 unknown nano-default-editor-5.8-3.fc34.noarch
|
||||
FEDORA-2021-d1fc0b9d32 Moderate/Sec. nettle-3.7.3-1.fc34.x86_64
|
||||
FEDORA-2021-97949d7a4e bugfix openldap-2.4.57-5.fc34.x86_64
|
||||
FEDORA-2021-e6916d6758 bugfix pam-1.5.1-6.fc34.x86_64
|
||||
FEDORA-2021-07931f7f08 bugfix python-setuptools-wheel-53.0.0-2.fc34.noarch
|
||||
FEDORA-2021-2056ce89d9 enhancement python-unversioned-command-3.9.6-1.fc34.noarch
|
||||
FEDORA-2021-d613e00b72 enhancement python-unversioned-command-3.9.6-2.fc34.noarch
|
||||
FEDORA-2021-2056ce89d9 enhancement python3-3.9.6-1.fc34.x86_64
|
||||
FEDORA-2021-d613e00b72 enhancement python3-3.9.6-2.fc34.x86_64
|
||||
FEDORA-2021-3b74285c43 bugfix python3-dnf-4.8.0-1.fc34.noarch
|
||||
FEDORA-2021-3b74285c43 bugfix python3-hawkey-0.63.1-1.fc34.x86_64
|
||||
FEDORA-2021-3b74285c43 bugfix python3-libcomps-0.1.17-1.fc34.x86_64
|
||||
FEDORA-2021-3b74285c43 bugfix python3-libdnf-0.63.1-1.fc34.x86_64
|
||||
FEDORA-2021-2056ce89d9 enhancement python3-libs-3.9.6-1.fc34.x86_64
|
||||
FEDORA-2021-d613e00b72 enhancement python3-libs-3.9.6-2.fc34.x86_64
|
||||
FEDORA-2021-07931f7f08 bugfix python3-setuptools-53.0.0-2.fc34.noarch
|
||||
FEDORA-2021-1db6330a22 unknown sssd-client-2.5.2-1.fc34.x86_64
|
||||
FEDORA-2021-3141f0eff1 bugfix systemd-248.6-1.fc34.x86_64
|
||||
FEDORA-2021-3141f0eff1 bugfix systemd-libs-248.6-1.fc34.x86_64
|
||||
FEDORA-2021-3141f0eff1 bugfix systemd-networkd-248.6-1.fc34.x86_64
|
||||
FEDORA-2021-3141f0eff1 bugfix systemd-pam-248.6-1.fc34.x86_64
|
||||
FEDORA-2021-3141f0eff1 bugfix systemd-rpm-macros-248.6-1.fc34.noarch
|
||||
FEDORA-2021-b8b1f6e54f enhancement vim-minimal-2:8.2.3182-1.fc34.x86_64
|
||||
FEDORA-2021-67645ae09f enhancement xkeyboard-config-2.33-1.fc34.noarch
|
||||
FEDORA-2021-3b74285c43 bugfix yum-4.8.0-1.fc34.noarch
|
||||
```
|
||||
|
||||
The output is in three columns. These show the ID for an update, the type of the update, and the package to which it refers.
|
||||
|
||||
If you want to see the Bodhi page for a specific update, just add the id to the end of this URL:
|
||||
<https://bodhi.fedoraproject.org/updates/>.
|
||||
|
||||
For example, <https://bodhi.fedoraproject.org/updates/FEDORA-2021-3141f0eff1> for _systemd-248.6-1.fc34.x86_64_ or <https://bodhi.fedoraproject.org/updates/FEDORA-2021-b09e010a46> for _coreutils-8.32-30.fc34.x86_64_.
|
||||
|
||||
The next command will list the actual changelog.
|
||||
|
||||
```
|
||||
dnf updateinfo info
|
||||
```
|
||||
|
||||
The output from this command is quite long. So only a few interesting excerpts are provided below.
|
||||
|
||||
Start with a small one:
|
||||
|
||||
```
|
||||
===============================================================================
|
||||
ca-certificates-2021.2.50-1.0.fc34
|
||||
===============================================================================
|
||||
Update ID: FEDORA-2021-1f32e18471
|
||||
Type: bugfix
|
||||
Updated: 2021-06-18 22:08:02
|
||||
Description: Update the ca-certificates list to the lastest upstream list.
|
||||
Severity: Low
|
||||
```
|
||||
|
||||
Notice how this info has the update ID, type, updated time, description and severity. Very simple and easy to understand.
|
||||
|
||||
Now look at the _systemd_ update which, in addition to the previous items, has some bugs associated with it in Red Hat Bugzilla, a more elaborate description, and a different severity.
|
||||
|
||||
```
|
||||
===============================================================================
|
||||
systemd-248.6-1.fc34
|
||||
===============================================================================
|
||||
Update ID: FEDORA-2021-3141f0eff1
|
||||
Type: bugfix
|
||||
Updated: 2021-07-24 22:00:30
|
||||
Bugs: 1963428 - if keyfile >= 1024*4096-1 service "systemd-cryptsetup@<partition name>" can't start
|
||||
: 1965815 - 50-udev-default.rules references group "sgx" which does not exist
|
||||
: 1975564 - systemd-cryptenroll SIGABRT when adding recovery key - buffer overflow
|
||||
: 1984651 - systemd[1]: Assertion 'a <= b' failed at src/libsystemd/sd-event/sd-event.c:2903, function sleep_between(). Aborting.
|
||||
Description: - Create 'sgx' group (and also use soft-static uids for input and render, see https://pagure.io/setup/c/df3194a7295c2ca3cfa923981b046f4bd2754825 and https://pagure.io/packaging-committee/issue/1078 (#1965815)
|
||||
: - Various bugfixes (#1963428, #1975564)
|
||||
: - Fix for a regression introduced in the previous release with sd-event abort (#1984651)
|
||||
:
|
||||
: No need to log out or reboot.
|
||||
Severity: Moderate
|
||||
```
|
||||
|
||||
Next look at a _curl_ update. This has a security update with several [CVE][5]s associated with it. Each CVE has its respective Red Hat Bugzilla bug.
|
||||
|
||||
```
|
||||
===============================================================================
|
||||
curl-7.76.1-7.fc34
|
||||
===============================================================================
|
||||
Update ID: FEDORA-2021-83fdddca0f
|
||||
Type: security
|
||||
Updated: 2021-07-22 22:03:07
|
||||
Bugs: 1984325 - CVE-2021-22922 curl: wrong content via metalink is not being discarded [fedora-all]
|
||||
: 1984326 - CVE-2021-22923 curl: Metalink download sends credentials [fedora-all]
|
||||
: 1984327 - CVE-2021-22924 curl: bad connection reuse due to flawed path name checks [fedora-all]
|
||||
: 1984328 - CVE-2021-22925 curl: Incorrect fix for CVE-2021-22898 TELNET stack contents disclosure [fedora-all]
|
||||
Description: - fix TELNET stack contents disclosure again (CVE-2021-22925)
|
||||
: - fix bad connection reuse due to flawed path name checks (CVE-2021-22924)
|
||||
: - disable metalink support to fix the following vulnerabilities
|
||||
: CVE-2021-22923 - metalink download sends credentials
|
||||
: CVE-2021-22922 - wrong content via metalink not discarded
|
||||
Severity: Moderate
|
||||
```
|
||||
|
||||
This item shows a simple enhancement update.
|
||||
|
||||
```
|
||||
===============================================================================
|
||||
python3-docs-3.9.6-1.fc34 python3.9-3.9.6-1.fc34
|
||||
===============================================================================
|
||||
Update ID: FEDORA-2021-2056ce89d9
|
||||
Type: enhancement
|
||||
Updated: 2021-07-08 22:00:53
|
||||
Description: Update of Python 3.9 and python3-docs to latest release 3.9.6
|
||||
Severity: None
|
||||
```
|
||||
|
||||
Finally an “unknown” type update.
|
||||
|
||||
```
|
||||
===============================================================================
|
||||
file-5.39-6.fc34
|
||||
===============================================================================
|
||||
Update ID: FEDORA-2021-07625b9c81
|
||||
Type: unknown
|
||||
Updated: 2021-06-11 22:16:57
|
||||
Bugs: 1963895 - Wrong detection of python bytecode mimetypes
|
||||
Description: do not classify python bytecode files as text (#1963895)
|
||||
Severity: None
|
||||
```
|
||||
|
||||
### Conclusion
|
||||
|
||||
So, in what situation does dnf updateinfo become handy?
|
||||
|
||||
Well, you could use it if you prefer managing updates fully via the CLI, or if you are unable to successfully use the GUI tools at a specific moment.
|
||||
|
||||
In which case is checking the changelog useful?
|
||||
|
||||
Say you manage the updates yourself, sometimes you might not consider it ideal to stop what you are doing to update your system. Instead of simply installing the updates, you check the changelogs. This allows you to figure out whether you should prioritize your updates (maybe there’s a important security fix?) or whether to postpone a bit longer (no important fix, “I will do it later when I’m not doing anything important”).
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/use-dnf-updateinfo-to-read-update-changelogs/
|
||||
|
||||
作者:[Mateus Rodrigues Costa][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/mateusrodcosta/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2021/08/dnf-updateinfo-816x345.jpg
|
||||
[2]: https://unsplash.com/@ffstop?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||
[3]: https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||
[4]: https://fedoramagazine.org/a-quick-introduction-to-toolbox-on-fedora/
|
||||
[5]: https://en.wikipedia.org/wiki/Common_Vulnerabilities_and_Exposures
|
@ -0,0 +1,138 @@
|
||||
[#]: subject: "What is SteamOS? Everything Important You Need to Know About This “Gaming Distribution”"
|
||||
[#]: via: "https://itsfoss.com/steamos/"
|
||||
[#]: author: "Ankush Das https://itsfoss.com/author/ankush/"
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
What is SteamOS? Everything Important You Need to Know About This “Gaming Distribution”
|
||||
======
|
||||
|
||||
SteamOS is a Linux-based operating system that aims to provide a seamless gaming experience from Steam’s own game store.
|
||||
|
||||
While it has been in existence for about a decade now, there are a few things that you should know about it.
|
||||
|
||||
In this article, we try to address most of the common questions regarding SteamOS.
|
||||
|
||||
### What is SteamOS?
|
||||
|
||||
SteamOS is a Linux distribution from the game distribution platform Steam. It is not a generic desktop operating system like Debian, Linux Mint or Ubuntu though you could use the desktop features. By default, SteamOS gives you a console like interface because SteamOS is intended to be the operating system on Steam devices like Steam Machine (discontinued) and Steam Deck.
|
||||
|
||||
![SteamOS interface][1]
|
||||
|
||||
While you can install the Steam game client on any Linux distribution and other platforms, SteamOS was developed to provide a console-like experience to play games from the Steam store.
|
||||
|
||||
### Which Linux distribution SteamOS is based on?
|
||||
|
||||
SteamOS is a Linux-based operating system originally based on Debian 8. With Valve’s new [Steam Deck][2] handheld gaming device, SteamOS’s latest version (SteamOS 3.0) uses Arch Linux as its base because of its rolling-release update schedule.
|
||||
|
||||
The developers believe that Arch Linux as a base for SteamOS is useful to push quick updates and optimize SteamOS for Steam Deck.
|
||||
|
||||
![][3]
|
||||
|
||||
### System requirements for SteamOS
|
||||
|
||||
Ideally, any machine with the following minimum requirements should work:
|
||||
|
||||
* Intel or AMD 64-bit capable processor
|
||||
* 4GB or more memory
|
||||
* 250GB or larger disk
|
||||
* NVIDIA, Intel, or AMD graphics card
|
||||
* USB port or DVD drive for installation
|
||||
|
||||
|
||||
|
||||
### Will SteamOS Work on your PC?
|
||||
|
||||
SteamOS (version 2.0) comes with drivers and chipsets that support a specific set of hardware.
|
||||
|
||||
It should theoretically work on every PC, but there’s no official support for the latest and greatest hardware.
|
||||
|
||||
### Is SteamOS just another Linux distribution?
|
||||
|
||||
SteamOS is technically one of the [gaming Linux distributions][4] available. But, unlike some others, it is not meant for a full-fledged desktop experience. While you have the ability to install Linux applications, it supports a limited number of packages.
|
||||
|
||||
In short, it is not suitable to replace a desktop Linux OS.
|
||||
|
||||
### Is Steam OS Actively Maintained?
|
||||
|
||||
**Yes** and **No.**
|
||||
|
||||
SteamOS is based on Debian 8 for a long time now with no updates whatsoever.
|
||||
|
||||
So, if you are looking to install it on your personal machine, the version available to the public (SteamOS 2.0) is not actively maintained.
|
||||
|
||||
However, Valve is actively working on SteamOS 3.0 for its Steam Deck hardware. Hence, there is a possibility that you should find it available soon enough for your desktop.
|
||||
|
||||
### Should You Prefer SteamOS for PC Gaming?
|
||||
|
||||
**No.** SteamOS is not a proper replacement for Windows or other Linux distributions.
|
||||
|
||||
While it was primarily tailored to play games, there are many caveats to know before you proceed.
|
||||
|
||||
### Do all games work on SteamOS?
|
||||
|
||||
**No.** SteamOS relies on the Proton compatibility layer to make most of the Windows-exclusive games work.
|
||||
|
||||
Of course, [Gaming on Linux][5] has been made possible with the same underlying tech, but at the time of writing this, you cannot make all the games available in Steam work with it.
|
||||
|
||||
Even though many games should work on it, that does not guarantee that all games you have in your library will work as expected.
|
||||
|
||||
If you are looking to play supported games and Linux-only games, you can consider trying it.
|
||||
|
||||
### Is SteamOS open source?
|
||||
|
||||
**Yes** (SteamOS 2.0).
|
||||
|
||||
The operating system is open-source, and you can find the source code in its [official repo][6].
|
||||
|
||||
But, the Steam client that you will be using on it is proprietary.
|
||||
|
||||
It is worth noting that SteamOS 3.0 is still a work in progress. So, you cannot find the source code or any progress of it available to the public.
|
||||
|
||||
### Is SteamOS free to use?
|
||||
|
||||
You won’t find the new SteamOS version available to the public yet, but it is essentially free to use. The older version based on Debian is available to download from the [official site][7].
|
||||
|
||||
### Can I find a gaming system with SteamOS built in?
|
||||
|
||||
![Steam Machine console has been doscontinued][8]
|
||||
|
||||
SteamOS was originally created to be the operating system on Steam’s very own PlayStation/Xbox styled console called Steam Machine. Released around 2015, Steam Machine did not see much success and was eventually discontinued.
|
||||
|
||||
Now the only device to feature SteamOS is the much-anticipated Steam Deck.
|
||||
|
||||
If SteamOS is available to download for other hardware, you may see commercial choices to have SteamOS pre-installed with a gaming machine.
|
||||
|
||||
But, for now, you should not believe any claims by unknown manufacturers to offer SteamOS out of the box.
|
||||
|
||||
### Will Next-gen SteamOS Make Linux a Viable Choice for Gaming?
|
||||
|
||||
Absolutely. Yes.
|
||||
|
||||
Linux may not be the recommended choice for gamers out there. You can explore if [we recommend Linux for gaming][9]. Still, if SteamOS evolves to support every game for its Steam Deck hardware, desktop Linux users can finally experience all unsupported Steam games.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/steamos/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2021/08/steamos.jpg?resize=800%2C450&ssl=1
|
||||
[2]: https://www.steamdeck.com/en/
|
||||
[3]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2021/08/steam-deck.jpg?resize=800%2C479&ssl=1
|
||||
[4]: https://itsfoss.com/linux-gaming-distributions/
|
||||
[5]: https://itsfoss.com/linux-gaming-guide/
|
||||
[6]: https://repo.steampowered.com/steamos/
|
||||
[7]: https://store.steampowered.com/steamos/
|
||||
[8]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2021/08/valves-steam-machine.jpg?resize=800%2C441&ssl=1
|
||||
[9]: https://news.itsfoss.com/linux-for-gaming-opinion/
|
105
translated/tech/20210608 Tune your MySQL queries like a pro.md
Normal file
105
translated/tech/20210608 Tune your MySQL queries like a pro.md
Normal file
@ -0,0 +1,105 @@
|
||||
[#]: subject: (Tune your MySQL queries like a pro)
|
||||
[#]: via: (https://opensource.com/article/21/5/mysql-query-tuning)
|
||||
[#]: author: (Dave Stokes https://opensource.com/users/davidmstokes)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (unigeorge)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
|
||||
如老手一般玩转 MySQL 查询
|
||||
======
|
||||
优化查询语句不过是一项简单的工程,而非什么高深的黑魔法。
|
||||
![woman on laptop sitting at the window][1]
|
||||
|
||||
许多人将数据库查询语句的调优视作哈利波特小说中某种神秘的“黑魔法”;使用错误的咒语,数据就会从宝贵的资源变成一堆糊状物。
|
||||
|
||||
实际上,对关系数据库系统的查询调优是一项简单的工程,其遵循的规则或启发式方法很容易理解。查询优化器会将你发送的查询指令转换为 [MySQL][2] 实例,然后将这些启发式方法和优化器已知的数据信息结合使用,确定获取所请求数据的最佳方式。再读一下最后一部分:_“优化器已知的数据信息_。”查询优化器需要对数据所在位置的猜测越少(即已知信息越多),它就可以越好地制定交付数据的计划。
|
||||
|
||||
为了让优化器更好地了解数据,你可以考虑使用索引和直方图。正确使用索引和直方图可以大大提高数据库查询的速度。这就像如果你按照食谱做菜,就可以得到你喜欢吃的东西;但是假如你随意在该食谱中添加材料,最终得到的东西可能就不那么尽如人意了。
|
||||
|
||||
### 基于成本的优化器
|
||||
|
||||
大多数现代关系型数据库使用基于成本的优化器(cost-based optimizer)来确定如何从数据库中检索数据。该成本方案是基于尽可能减少非常耗费资源的磁盘读取过程。数据库服务器上的查询优化器代码会在遇到数据时保留有关获取该数据的统计信息,并构建获取数据所需时间的历史模型。
|
||||
|
||||
但历史数据是可能会过时的。这就好像你去商店买你最喜欢的零食,然后突然发现零食涨价或者商店关门了。服务器的优化进程可能会根据旧信息做出错误的假设,进而制定出低效的查询计划。
|
||||
|
||||
查询的复杂性可能会影响优化。优化器希望提供可用的最低成本查询方式。连接五个不同的表就意味着有 5 的阶乘即 120 种可能的连接顺序组合。代码中内置了启发式方法,以尝试对所有可能的选项进行快捷评估。 MySQL 每次看到查询时都希望生成一个新的查询计划,而其他数据库(例如 Oracle)则可以锁定查询计划。这就是向优化器提供有关数据的详细信息至关重要的原因。要想获得稳定的性能,在制定查询计划时为查询优化器提供最新信息确实很有效。
|
||||
|
||||
此外,优化器中内置的规则可能与数据的实际情况并不相符。没有更多有效信息的情况下,查询优化器会假设列中的所有数据均匀分布在所有行中。没有其他选择依据时,它会默认选择两个可能索引中较小的一个。虽然基于成本的优化器模型可以制定出很多好的决策,但最终查询计划并不是最佳方案的情况也是有可能的。
|
||||
|
||||
### 查询计划是神马?
|
||||
|
||||
查询计划(query plan)是指优化器基于查询语句产生的,提供给服务器执行的计划内容。查看查询计划的方法是在查询语句前加上 `EXPLAIN` 关键字。例如,以下查询要从城市表(city)和相应的国家表(country)中获得城市名称(和所属国家名称),城市表和国家表通过国家唯一代码连接。本例中仅查询了英国的字母顺序前五名的城市:
|
||||
|
||||
```
|
||||
SELECT city.name AS 'City',
|
||||
country.name AS 'Country'
|
||||
FROM city
|
||||
JOIN country ON (city.countrycode = country.code)
|
||||
WHERE country.code = 'GBR'
|
||||
LIMIT 5;
|
||||
```
|
||||
|
||||
在查询语句前加上 `EXPLAIN` 可以看到优化器生成的查询计划。跳过除输出末尾之外的所有内容,可以看到优化后的查询:
|
||||
|
||||
```
|
||||
SELECT `world`.`city`.`Name` AS `City`,
|
||||
'United Kingdom' AS `Country`
|
||||
FROM `world`.`city`
|
||||
JOIN `world`.`country`
|
||||
WHERE (`world`.`city`.`CountryCode` = 'GBR')
|
||||
LIMIT 5;
|
||||
```
|
||||
|
||||
看下比较大的几个变化, `country.name as 'Country'` 改成了 `'United Kingdom' AS 'Country'`,`WHERE` 子句从在国家表中查找变成了在城市表中查找。优化器认为这两个改动会提供比原始查询更快的结果。
|
||||
|
||||
### 索引
|
||||
|
||||
在MySQL世界中,你会听到索引或键的概念。不过,索引是由键组成的,键是一种识别记录的方式,并且大概率是唯一的。如果将列设计为键,优化器可以搜索这些键的列表以找到所需的记录,而无需读取整个表。如果没有索引,服务器必须从第一列的第一行开始读取每一行数据。如果该列是作为唯一索引创建的,则服务器可以直接读取该行数据并忽略其余数据。索引的值(也称为基数)唯一性越强越好。请记住,我们在寻找更快获取数据的方法。
|
||||
|
||||
MySQL 默认的 InnoDB 存储引擎希望你的表有一个主键,并将通过该键将你的数据存储在 B+ 树中。不可见列是 MySQL 最近添加的功能——除非在查询中明确指明该不可见列,否则不会返回该列数据。例如,`SELECT * FROM foo;` 就不会返回任何不可见列。这个功能提供了一种向旧表添加主键的方法,且无需为了包含该新列而重写所有查询语句。
|
||||
|
||||
更复杂的是,有多种类型的索引,例如函数索引、空间索引和复合索引。甚至在某些情况下,你还可以创建这样一个索引:该索引可以为查询提供所有请求的信息,从而无需再去访问数据表。
|
||||
|
||||
本文不会详细讲解各种索引类型,你只需将索引看作指向要查询的数据记录的快捷方式。你可以在一个或多个列或这些列的一部分上创建索引。我的医师系统就可以通过我姓氏的前三个字母和出生日期来查找我的记录。使用多列时要注意首选唯一性最强的字段,然后是第二强的字段,依此类推。年-月-日的索引可用于年-月-日、年-月和年搜索,但不适用于日、月-日或年-日搜索。考虑这些因素有助于你围绕如何使用数据这一出发点来设计索引。
|
||||
|
||||
### 直方图
|
||||
|
||||
直方图就是数据的分布式。如果你将人名按其姓氏的字母顺序排序,就可以对姓氏以字母 A 到 F 开头的人放到一个“逻辑桶”中,然后将 G 到 J 开头的放到另一个中,依此类推。优化器会假定数据在列内均匀分布,但实际使用时多数情况并不是均匀的。
|
||||
|
||||
MySQL 提供两种类型的直方图:所有数据在桶中平均分配的等高型,以及单个值在单个桶中的等宽型。最多可以设置 1,024 个存储桶。 数据存储桶数量的选择取决于许多因素,包括去重后的数值量、数据倾斜度以及需要的结果准确度。如果桶的数量超过某个阈值,桶机制带来的收益就会开始递减。
|
||||
|
||||
以下命令将在表 t 的列 c1 上创建 10 个桶的直方图:
|
||||
|
||||
```
|
||||
ANALYZE TABLE t UPDATE HISTOGRAM ON c1 WITH 10 BUCKETS;
|
||||
```
|
||||
|
||||
想象一下你在售卖小号、中号和大号袜子,每种尺寸的袜子都放在单独的储物箱中。如果你想找某个尺寸的袜子,就可以直接去对应尺寸的箱子里找。MySQL 自从三年前发布 MySQL 8.0 以来就有了直方图功能,但该功能却并没有像索引那样广为人知。与索引不同,使用直方图插入、更新或删除记录都不会产生额外开销。而如果更新索引,就必须更新 `ANALYZE TABLE` 命令。 当数据变动不大并且频繁更改数据会降低效率时,直方图是一种很好的方法。
|
||||
|
||||
### 选择索引还是直方图?
|
||||
|
||||
对需要直接访问的且具备唯一性的数据项目使用索引。虽然修改、删除和插入操作会产生额外开销,但如果数据架构正确,索引就可以方便你快速访问。对不经常更新的数据则建议使用直方图,例如过去十几年的季度结果。
|
||||
|
||||
### 结语
|
||||
|
||||
本文源于最近在 [Open Source 101 会议][3] 上的一次报告。报告的演示文稿源自 [PHP UK Conferenc][4] 的研讨会。查询调优是一个复杂的话题,每次我就索引和直方图作报告时,我都会找到新的可改进点。但是每次报告反馈也表明很多软件界中的人并不精通索引,并且时常使用错误。我想直方图大概由于出现时间较短,还没有出现像索引这种使用错误的情况。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/5/mysql-query-tuning
|
||||
|
||||
作者:[Dave Stokes][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[unigeorge](https://github.com/unigeorge)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/davidmstokes
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/lenovo-thinkpad-laptop-window-focus.png?itok=g0xPm2kD (young woman working on a laptop)
|
||||
[2]: https://www.mysql.com/
|
||||
[3]: https://opensource101.com/
|
||||
[4]: https://www.phpconference.co.uk/
|
Loading…
Reference in New Issue
Block a user