本篇文章將向大家介紹數(shù)據(jù)庫中索引類型和使用場合,本文以SQL Server為例,對于其他技術(shù)平臺的朋友也是有參考價值的,原理差不多。 查詢數(shù)據(jù)時索引使數(shù)據(jù)庫引擎執(zhí)行速度更快,有針對性的數(shù)據(jù)檢索,而不是簡單地整表掃描(Full table scan)。 為了有效的使用索引,我們必須對索引的構(gòu)成有所了解
通常我們要對數(shù)據(jù)庫進行優(yōu)化,主要可以通過以下五種方法。
本篇文章將向大家介紹數(shù)據(jù)庫中索引類型和使用場合,本文以SQL Server為例,對于其他技術(shù)平臺的朋友也是有參考價值的,原理差不多。
查詢數(shù)據(jù)時索引使數(shù)據(jù)庫引擎執(zhí)行速度更快,有針對性的數(shù)據(jù)檢索,而不是簡單地整表掃描(Full table scan)。
為了有效的使用索引,我們必須對索引的構(gòu)成有所了解,而且我們知道在數(shù)據(jù)表中添加索引必然需要創(chuàng)建和維護索引表,所以我們要全局地衡量添加索引是否能提高數(shù)據(jù)庫系統(tǒng)的查詢性能。
在物理層面上,數(shù)據(jù)庫由數(shù)據(jù)文件組成,而這些數(shù)據(jù)文件組成文件組,然后存儲在磁盤上。每個文件包含許多區(qū),每個區(qū)的大小為64K由八個物理上連續(xù)的頁組成(一個頁8K),我們知道 頁是SQL Server數(shù)據(jù)庫中的數(shù)據(jù)存儲的基本單位 。為數(shù)據(jù)庫中的數(shù)據(jù)文件(.mdf 或 .ndf)分配的磁盤空間可以從邏輯上劃分成頁(從0到n連續(xù)編號)。
頁中存儲的類型有: 數(shù)據(jù) , 索引 和 溢出 。
在SQL Server中,通過文件組這個邏輯對象對存放數(shù)據(jù)的文件進行管理。
在頂層是我們的數(shù)據(jù)庫,由于數(shù)據(jù)庫是由一個或多個文件組組成,而文件組是由一個或多個文件組成的 邏輯組 ,所以我們可以把文件組分散到不同的磁盤中,使用戶數(shù)據(jù)盡可能跨越多個設(shè)備,多個I/O 運轉(zhuǎn),避免 I/O 競爭,從而均衡I/O負載,克服訪問瓶頸。
如下圖所示,文件是由區(qū)組成的,而區(qū)由八個物理上連續(xù)的頁組成,由于區(qū)的大小為64K,所以每當(dāng)增加一個區(qū)文件就增加64K。
頁中保存的數(shù)據(jù)類型有:表數(shù)據(jù)、索引數(shù)據(jù)、溢出數(shù)據(jù)、分配映射、頁空閑空間、索引分配等。
頁類型 | 內(nèi)容 |
---|---|
Data | 當(dāng) text in row 設(shè)置為 ON 時,包含除 text、 ntext、image、nvarchar(max)、varchar(max)、varbinary(max) 和 xml 數(shù)據(jù)之外的所有數(shù)據(jù)的數(shù)據(jù)行。 |
Index | 索引條目。 |
Text/Image | 大型對象數(shù)據(jù)類型:text 、 ntext、image、nvarchar(max)、varchar(max)、varbinary(max) 和 xml 數(shù)據(jù)。數(shù)據(jù)行超過 8 KB 時為可變長度數(shù)據(jù)類型列:varchar 、nvarchar、varbinary 和 sql_variant |
Global Allocation Map、Shared Global Allocation Map | 有關(guān)區(qū)是否分配的信息。 |
Page Free Space | 有關(guān)頁分配和頁的可用空間的信息。 |
Index Allocation Map | 有關(guān)每個分配單元中表或索引所使用的區(qū)的信息。 |
Bulk Changed Map | 有關(guān)每個分配單元中自最后一條 BACKUP LOG 語句之后的大容量操作所修改的區(qū)的信息。 |
Differential Changed Map | 有關(guān)每個分配單元中自最后一條 BACKUP DATABASE 語句之后更改的區(qū)的信息。 |
在數(shù)據(jù)頁上,數(shù)據(jù)行緊接著頁頭(標(biāo)頭)按順序放置;頁頭包含標(biāo)識值,如頁碼或?qū)ο髷?shù)據(jù)的對象ID;數(shù)據(jù)行持有實際的數(shù)據(jù);最后,頁的末尾是行偏移表,對于頁中的每一行,每個行偏移表都包含一個條目,每個條目記錄對應(yīng)行的第一個字節(jié)與 頁頭 的距離,行偏移表中的條目的順序與頁中行的順序相反。
“索引(Index)提供查詢的速度”這是對索引的最基本的解釋,接下來我們將通過介紹索引的組成,讓大家對索引有更深入的理解。
索引是數(shù)據(jù)庫中的一個獨特的結(jié)構(gòu),由于它保存數(shù)據(jù)庫信息,那么我們就需要給它分配磁盤空間和維護索引表。創(chuàng)建索引并不會改變表中的數(shù)據(jù),它只是創(chuàng)建了一個新的數(shù)據(jù)結(jié)構(gòu)指向數(shù)據(jù)表;打個比方,平時我們使用字典查字時,首先我們要知道查詢單詞起始字母,然后翻到目錄頁,接著查找單詞具體在哪一頁,這時我們目錄就是索引表,而目錄項就是索引了。
當(dāng)然,索引比字典目錄更為復(fù)雜,因為數(shù)據(jù)庫必須處理插入,刪除和更新等操作,這些操作將導(dǎo)致索引發(fā)生變化。
葉節(jié)點
假設(shè)我們磁盤上的數(shù)據(jù)是物理有序的,那么數(shù)據(jù)庫在進行插入,刪除和更新操作時,必然會導(dǎo)致數(shù)據(jù)發(fā)生變化,如果我們要保存數(shù)據(jù)的連續(xù)和有序,那么我們就需要移動數(shù)據(jù)的物理位置,這將增大磁盤的I/O,使得整個數(shù)據(jù)庫運行非常緩慢;使用索引的主要目的是使數(shù)據(jù)邏輯有序,使數(shù)據(jù)獨立于物理有序存儲。
為了實現(xiàn)數(shù)據(jù)邏輯有序,索引使用雙向鏈表的數(shù)據(jù)結(jié)構(gòu)來保持數(shù)據(jù)邏輯順序,如果要在兩個節(jié)點中插入一個新的節(jié)點只需修改節(jié)點的前驅(qū)和后繼,而且無需修改新節(jié)點的物理位置。
雙向鏈表 (Doubly linked list)也叫雙鏈表,是 鏈表 的一種,它的每個數(shù)據(jù)結(jié)點中都有兩個指針,分別指向直接后繼和直接前驅(qū)。所以,從雙向鏈表中的任意一個結(jié)點開始,都可以很方便地訪問它的前驅(qū)結(jié)點和后繼結(jié)點。
理論上說,從雙向鏈表中刪除一個元素操作的時間復(fù)雜度是O(1),如果希望刪除一個具體有給定關(guān)鍵字的元素,那么最壞的情況下的時間復(fù)雜度為O(n)。
在刪除的過程中,我們只需要將要刪除的節(jié)點的前節(jié)點和后節(jié)點相連,然后將要刪除的節(jié)點的前節(jié)點和后節(jié)點置為null即可。
//偽代碼
node.prev.next=node.next;
node.next.prev=node.prev;
node.prev=node.next=null;
如上圖,索引葉節(jié)點包含索引值和相應(yīng)的RID(ROWID),而且葉節(jié)點通過雙向鏈表有序地連接起來;同時我們主要到數(shù)據(jù)表不同于索引葉節(jié)點,表中的數(shù)據(jù)無序存儲,它們不全是存儲在同一表塊中,而且塊之間不存在連接。
總的來說,索引保存著具體數(shù)據(jù)的物理地址值。
索引的類型主要有兩種: 聚集索引 和 非聚集索引 。
聚集索引 :物理存儲按照索引排序。
指數(shù)據(jù)庫表行中數(shù)據(jù)的物理順序與鍵值的邏輯(索引)順序相同。一個表只能有一個聚集索引,因為一個表的物理順序只有一種情況,所以,對應(yīng)的聚集索引只能有一個。如果某索引不是聚集索引,則表中的行物理順序與索引順序不匹配,與非聚集索引相比,聚集索引有著更快的檢索速度。
非聚集索引 :物理存儲不按照索引排序。
該索引中索引的邏輯順序與磁盤上行的物理存儲順序不同,一個表中可以擁有多個非聚集索引。除了聚集索引以外的索引都是非聚集索引,只是人們想細分一下非聚集索引,分成普通索引,唯一索引,全文索引。如果非要把非聚集索引類比成現(xiàn)實生活中的東西,那么非聚集索引就像新華字典的偏旁字典,他結(jié)構(gòu)順序與實際存放順序不一定一致。
聚集索引 的數(shù)據(jù)頁是物理有序地存儲,數(shù)據(jù)頁是聚集索引的葉節(jié)點,數(shù)據(jù)頁之間通過雙向鏈表的形式連接起來,而且實際的數(shù)據(jù)都存儲在數(shù)據(jù)頁中。當(dāng)我們給表添加索引后,表中的數(shù)據(jù)將根據(jù)索引進行排序。
假設(shè)我們有一個表T_Pet,它包含四個字段分別是:animal,name,sex和age,而且使用animal作為索引列,具體SQL代碼如下:
-----------------------------------------------------------
---- Create T_Pet table in tempdb.
-----------------------------------------------------------
USE tempdb
CREATE TABLE T_Pet
(
animal VARCHAR(20),
[name] VARCHAR(20),
sex CHAR(1),
age INT
)
CREATE UNIQUE CLUSTERED INDEX T_PetonAnimal1_ClterIdx ON T_Pet (animal)
-----------------------------------------------------------
---- Insert data into data table.
-----------------------------------------------------------
DECLARE @i int
SET @i=0
WHILE(@i<1000000)
BEGIN
INSERT INTO T_Pet (
animal,
[name],
sex,
age
)
SELECT [dbo].random_string(11) animal,
[dbo].random_string(11) [name],
'F' sex,
cast(floor(rand()*5) as int) age
SET @i=@i+1
END
INSERT INTO T_Pet VALUES('Aardark', 'Hello', 'F', 1)
INSERT INTO T_Pet VALUES('Cat', 'Kitty', 'F', 2)
INSERT INTO T_Pet VALUES('Horse', 'Ma', 'F', 1)
INSERT INTO T_Pet VALUES('Turtles', 'SiSi', 'F', 4)
INSERT INTO T_Pet VALUES('Dog', 'Tomma', 'F', 2)
INSERT INTO T_Pet VALUES('Donkey', 'YoYo', 'F', 3)
如上圖所示,從左往右的第一和第二層是索引頁,第三層是數(shù)據(jù)頁(葉節(jié)點),數(shù)據(jù)頁之間通過雙向鏈表連接起來,而且數(shù)據(jù)頁中的數(shù)據(jù)根據(jù)索引排序;假設(shè),我們要查找名字(name)為Xnnbqba的動物Ifcey,這里我們以animal作為表的索引,所以數(shù)據(jù)庫首先根據(jù)索引查找,當(dāng)找到索引值animal = ‘Ifcey時,接著查找該索引的數(shù)據(jù)頁(葉節(jié)點)獲取具體數(shù)據(jù)。具體的查詢語句如下:
SET STATISTICS PROFILE ON
SET STATISTICS TIME ON
SELECT animal, [name], sex, age
FROM T_Pet
WHERE animal = 'Ifcey'
SET STATISTICS PROFILE OFF
SET STATISTICS TIME OFF
當(dāng)我們執(zhí)行完SQL查詢計劃時,把鼠標(biāo)指針放到“聚集索引查找”上,這時會出現(xiàn)如下圖信息,我們可以查看到一個重要的信息Logical Operation——Clustered Index Seek,SQL查詢是直接根據(jù)聚集索引獲取記錄,查詢速度最快。
從下圖查詢結(jié)果,我們發(fā)現(xiàn)查詢步驟只有2步,首先通過Clustered Index Seek快速地找到索引Ifcey,接著查詢索引的葉節(jié)點(數(shù)據(jù)頁)獲取數(shù)據(jù)。
查詢執(zhí)行時間:CPU 時間= 0 毫秒,占用時間= 1 毫秒。
現(xiàn)在我們把表中的索引刪除,重新執(zhí)行查詢計劃,這時我們可以發(fā)現(xiàn)Logical Operation已經(jīng)變?yōu)門able Scan,由于表中有100萬行數(shù)據(jù),這時查詢速度就相當(dāng)緩慢。
從下圖查詢結(jié)果,我們發(fā)現(xiàn)查詢步驟變成3步了,首先通過Table Scan查找animal = ‘Ifcey’,在執(zhí)行查詢的時候,SQL Server會自動分析SQL語句,而且它估計我們這次查詢比較耗時,所以數(shù)據(jù)庫進行并發(fā)操作加快查詢的速度。
查詢執(zhí)行時間:CPU 時間= 329 毫秒,占用時間= 182 毫秒。
通過上面的有聚集索引和沒有的對比,我們發(fā)現(xiàn)了查詢性能的差異,如果使用索引數(shù)據(jù)庫首先查找索引,而不是漫無目的的全表遍歷。
在沒有聚集索引的情況下,表中的數(shù)據(jù)頁是通過堆(Heap)形式進行存儲,堆是不含聚集索引的表;SQL Server中的堆存儲是把新的數(shù)據(jù)行存儲到最后一個頁中。
非聚集索引 是物理存儲不按照索引排序,非聚集索引的葉節(jié)點(Index leaf pages)包含著指向具體數(shù)據(jù)行的 指針 或 聚集索引 ,數(shù)據(jù)頁之間沒有連接是相對獨立的頁。
假設(shè)我們有一個表T_Pet,它包含四個字段分別是:animal,name,sex和age,而且使用animal作為非索引列,具體SQL代碼如下:
-----------------------------------------------------------
---- Create T_Pet table in tempdb with NONCLUSTERED INDEX.
-----------------------------------------------------------
USE tempdb
CREATE TABLE T_Pet
(
animal VARCHAR(20),
[name] VARCHAR(20),
sex CHAR(1),
age INT
)
CREATE UNIQUE NONCLUSTERED INDEX T_PetonAnimal1_NonClterIdx ON T_Pet (animal)
接著我們要查詢表中animal = ‘Cat’的寵物信息,具體的SQL代碼如下:
SET STATISTICS PROFILE ON
SET STATISTICS TIME ON
SELECT animal, [name], sex, age
FROM T_Pet
WHERE animal = 'Cat'
SET STATISTICS PROFILE OFF
SET STATISTICS TIME OFF
如下圖所示,我們發(fā)現(xiàn)查詢計劃的最右邊有兩個步驟:RID和索引查找。由于這兩種查找方式相對于聚集索引查找要慢(Clustered Index Seek)。
首先SQL Server查找索引值,然后根據(jù)RID查找數(shù)據(jù)行,直到找到符合查詢條件的結(jié)果。
查詢執(zhí)行時間:CPU 時間= 0 毫秒,占用時間= 1 毫秒
由于堆是不含聚集索引的表,所以非聚集索引的葉節(jié)點將包含指向具體數(shù)據(jù)行的指針。
以前面的T_Pet表為例,假設(shè)T_Pet使用animal列作為非聚集索引,那么它的堆表非聚集索引結(jié)構(gòu)如下圖所示:
通過上圖,我們發(fā)現(xiàn)非聚集索引通過雙向鏈表連接,而且葉節(jié)點包含指向具體數(shù)據(jù)行的指針。
如果我們要查找animal = ‘Dog’的信息,首先我們遍歷第一層索引,然后數(shù)據(jù)庫判斷Dog屬于Cat范圍的索引,接著遍歷第二層索引,然后找到Dog索引獲取其中的保存的指針信息,根據(jù)指針信息獲取相應(yīng)數(shù)據(jù)頁中的數(shù)據(jù),接下來我們將通過具體的例子說明。
現(xiàn)在我們創(chuàng)建表employees,然后給該表添加 堆表非聚集索引 ,具體SQL代碼如下:
USE tempdb
---- Creates a sample table.
CREATE TABLE employees (
employee_id NUMERIC NOT NULL,
first_name VARCHAR(1000) NOT NULL,
last_name VARCHAR(900) NOT NULL,
date_of_birth DATETIME ,
phone_number VARCHAR(1000) NOT NULL,
junk CHAR(1000) ,
CONSTRAINT employees_pk PRIMARY KEY NONCLUSTERED (employee_id)
);
GO
現(xiàn)在我們查找employee_id = 29976的員工信息。
SELECT *
FROM employees
WHERE employee_id = 29976
查詢計劃如下圖所示:
首先,查找索引值employee_id = ‘29976’的索引,然后根據(jù)RID查找符合條件的數(shù)據(jù)行;所以說,堆表索引的查詢效率不如聚集表,接下來我們將介紹聚集表的非聚集索引。
當(dāng)表上存在聚集索引時,任何非聚集索引的葉節(jié)點不再是包含指針值,而是包含聚集索引的索引值。
以前面的T_Pet表為例,假設(shè)T_Pet使用animal列作為非聚集索引,那么它的索引表非聚集索引結(jié)構(gòu)如下圖所示:
通過上圖,我們發(fā)現(xiàn)非聚集索引通過雙向鏈表連接,而且葉節(jié)點包含索引表的索引值。
如果我們要查找animal = ‘Dog’的信息,首先我們遍歷第一層索引,然后數(shù)據(jù)庫判斷Dog屬于Cat范圍的索引,接著遍歷第二層索引,然后找到Dog索引獲取其中的保存的索引值,然后根據(jù)索引值獲取相應(yīng)數(shù)據(jù)頁中的數(shù)據(jù)。
接下來我們修改之前的employees表,首先我們刪除之前的堆表非聚集索引,然后增加索引表的非聚集索引,具體SQL代碼如下:
ALTER TABLE employees
DROP CONSTRAINT employees_pk
ALTER TABLE employees
ADD CONSTRAINT employees_pk PRIMARY KEY CLUSTERED (employee_id)
GO
SELECT * FROM employees
WHERE employee_id=29976
SQL Server每執(zhí)行一個查詢,首先要檢查該查詢是否存在執(zhí)行計劃,如果沒有,則要生成一個執(zhí)行計劃,那么什么是執(zhí)行計劃呢?簡單來說,它能幫助SQL Server制定一個最優(yōu)的查詢計劃。
下面我們將通過具體的例子說明SQL Server中索引的使用,首先我們定義一個表testIndex,它包含三個字段testIndex,bitValue和filler,具體的SQL代碼如下:
-----------------------------------------------------------
---- Index Usefulness sample
-----------------------------------------------------------
CREATE TABLE testIndex
(
testIndex int identity(1,1) constraint PKtestIndex primary key,
bitValue bit,
filler char(2000) not null default (replicate('A',2000))
)
CREATE INDEX XtestIndex_bitValue on testIndex(bitValue)
GO
INSERT INTO testIndex(bitValue)
VALUES (0)
GO 20000 --runs current batch 20000 times.
INSERT INTO testIndex(bitValue)
VALUES (1)
GO 10 --puts 10 rows into table with value 1
接著我們查詢表中bitValue = 0的數(shù)據(jù)行,而且表中bitValue = 0的數(shù)據(jù)有2000行。
SELECT *
FROM testIndex
WHERE bitValue = 0
現(xiàn)在我們查詢bitValue = 1的數(shù)據(jù)行。
SELECT *
FROM testIndex
WHERE bitValue = 1
現(xiàn)在我們注意到對同一個表不同數(shù)據(jù)查詢,居然執(zhí)行截然不同的查詢計劃,這究竟是什么原因?qū)е碌哪兀?
我們可以通過使用DBCC SHOW_STATISTICS查看到表中索引的詳細使用情況,具體SQL代碼如下:
UPDATE STATISTICS dbo.testIndex
DBCC SHOW_STATISTICS('dbo.testIndex', 'XtestIndex_bitValue')
WITH HISTOGRAM
通過上面的直方圖,我們知道SQL Server估計bitValue = 0數(shù)據(jù)行行有約19989行,而bitValue = 1估計約21;SQL Server優(yōu)化器根據(jù)數(shù)據(jù)量估算值,采取不同的執(zhí)行計劃,從而到達最優(yōu)的查詢性能,由于bitValue = 0數(shù)據(jù)量大,SQL Server只能提供掃描聚集索引獲取相應(yīng)數(shù)據(jù)行,而bitValue = 1實際數(shù)據(jù)行只有10行,SQL Server首先通過鍵查找bitValue = 1的數(shù)據(jù)行,然后嵌套循環(huán)聯(lián)接到聚集索引獲得余下數(shù)據(jù)行。
優(yōu)點
第一,通過創(chuàng)建唯一性索引,可以保證數(shù)據(jù)庫表中每一行數(shù)據(jù)的唯一性。
第二,可以大大加快 數(shù)據(jù)的檢索速度,這也是創(chuàng)建索引的最主要的原因。
第三,可以加速表和表之間的連接,特別是在實現(xiàn)數(shù)據(jù)的參考完整性方面特別有意義。
第四,在使用分組和排序 子句進行數(shù)據(jù)檢索時,同樣可以顯著減少查詢中分組和排序的時間。
第五,通過使用索引,可以在查詢的過程中,使用優(yōu)化隱藏器,提高系統(tǒng)的性能。
缺點
第一,創(chuàng)建索引和維護索引要耗費時間,這種時間隨著數(shù)據(jù)量的增加而增加。
第二,索引需要占物理空間,除了數(shù)據(jù)表占數(shù)據(jù)空間之外,每一個索引還要占一定的物理空間,如果要建立聚簇索引,那么需要的空間就會更大。
第三,當(dāng)對表中的數(shù)據(jù)進行增加、刪除和修改的時候,索引也要動態(tài)的維護,這樣就降低了數(shù)據(jù)的維護速度。
1.定義主鍵的數(shù)據(jù)列一定要建立索引。
2.定義有外鍵的數(shù)據(jù)列一定要建立索引。
3.對于經(jīng)常查詢的數(shù)據(jù)列最好建立索引。
4.對于需要在指定范圍內(nèi)的快速或頻繁查詢的數(shù)據(jù)列;
5.經(jīng)常用在WHERE子句中的數(shù)據(jù)列。
6.經(jīng)常出現(xiàn)在關(guān)鍵字order by、group by、distinct后面的字段。如果建立的是復(fù)合索引,索引的字段順序要和這些關(guān)鍵字后面的字段順序一致,否則索引不會被使用。
7.對于那些查詢中很少涉及的列,重復(fù)值比較多的列不要建立索引。
8.對于定義為text、image和bit的數(shù)據(jù)類型的列不要建立索引。
9.對于經(jīng)常存取的列不要建立索引
10.限制表上的索引數(shù)目。對一個存在大量更新操作的表,所建索引的數(shù)目一般不要超過3個,最多不要超過5個。索引雖說提高了訪問速度,但太多索引會影響數(shù)據(jù)的更新操作。
11.對復(fù)合索引,按照字段在查詢條件中出現(xiàn)的頻度建立索引。在復(fù)合索引中,記錄首先按照第一個字段排序。對于在第一個字段上取值相同的記錄,系統(tǒng)再按照第二個字段的取值排序,以此類推。因此只有復(fù)合索引的第一個字段出現(xiàn)在查詢條件中,該索引才可能被使用,因此將應(yīng)用頻度高的字段,放置在復(fù)合索引的前面,會使系統(tǒng)最大可能地使用此索引,發(fā)揮索引的作用。
索引 - SQL Server | Microsoft Learn
聚集與非聚集索引 - SQL Server | Microsoft Learn
《ORACLE PL/SQL編程詳解》全原創(chuàng)(共八篇)--系列文章導(dǎo)航
8 種主流數(shù)據(jù)遷移工具技術(shù)選型
SQLServer中的CTE(Common Table Expression)通用表表達式使用詳解
[推薦推薦]ORACLE SQL:經(jīng)典查詢練手系列文章收尾(目錄篇)
國思RDIF低代碼快速開發(fā)平臺(支持vue2、vue3)
機器學(xué)習(xí):神經(jīng)網(wǎng)絡(luò)構(gòu)建(下)
閱讀華為Mate品牌盛典:HarmonyOS NEXT加持下游戲性能得到充分釋放
閱讀實現(xiàn)對象集合與DataTable的相互轉(zhuǎn)換
閱讀算法與數(shù)據(jù)結(jié)構(gòu) 1 - 模擬
閱讀5. Spring Cloud OpenFeign 聲明式 WebService 客戶端的超詳細使用
閱讀Java代理模式:靜態(tài)代理和動態(tài)代理的對比分析
閱讀Win11筆記本“自動管理應(yīng)用的顏色”顯示規(guī)則
閱讀本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權(quán),請發(fā)郵件[email protected]
湘ICP備2022002427號-10 湘公網(wǎng)安備:43070202000427號© 2013~2025 haote.com 好特網(wǎng)