簡(jiǎn)單了解django索引的相關(guān)知識(shí)
前言
由于數(shù)據(jù)庫(kù)每天都用來(lái)存儲(chǔ)越來(lái)越多的信息,因此這些也是每個(gè)Django項(xiàng)目中的關(guān)鍵組件。 因此了解它們的工作方式非常重要。
當(dāng)然,我無(wú)法解釋所有可用于Django的不同數(shù)據(jù)庫(kù)的全部細(xì)節(jié)。 不僅如此,因?yàn)槲也恢肋@一切,但也因?yàn)檫@會(huì)造成一場(chǎng)談話(huà)。 或者可能是整個(gè)會(huì)議。
關(guān)于數(shù)據(jù)庫(kù)的理論背景我唯一想說(shuō)的是,有一種叫做“關(guān)系代數(shù)”的東西。 用你可能想出的每 一條SELECT語(yǔ)句都可以表達(dá)出來(lái)。 數(shù)學(xué)證明。
數(shù)據(jù)庫(kù)查找如何工作
相反,讓我們從數(shù)據(jù)庫(kù)查找的工作方式開(kāi)始。 因?yàn)槟鞘俏覀冏疃嗟臅r(shí)間。
假設(shè)我們有這個(gè)數(shù)據(jù)庫(kù)的人名表及其相應(yīng)的年齡,當(dāng)他們開(kāi)始編程時(shí)。
現(xiàn)在我們要選擇每個(gè)19歲開(kāi)始的人。

我們可以用SQL查詢(xún)來(lái)表達(dá):
SELECT * FROM people WHERE age = 19
現(xiàn)在,我們?nèi)绾握业矫總€(gè)人匹配該查詢(xún)?
表掃描查找
那么,這很容易。 我們只查看表中的每一行,檢查條件是否適用,如果是,則返回該行。
這被稱(chēng)為“全表掃描”
到現(xiàn)在為止還挺好。 我們?cè)谶@里有7排。 因此我們看7行。 這是少量的行,所以查詢(xún)速度很快。

但是想象一下,你有10萬(wàn),1億,100億甚至更多的行。 遍歷每一行可能非常耗時(shí)。 這不是我們想要的或我們可以負(fù)擔(dān)得起的東西。 我們希望能夠提供有保證的時(shí)機(jī)來(lái)查找特定行。 與行數(shù)無(wú)關(guān)。
這是索引加入派對(duì)的地方。
什么是索引?
對(duì)于數(shù)量不斷增加的數(shù)據(jù),索引可以快速訪(fǎng)問(wèn)單個(gè)(或多個(gè))項(xiàng)目,而不會(huì)減少很多速度。 這也被稱(chēng)為“隨機(jī)訪(fǎng)問(wèn)”。 你會(huì)看到為什么這樣叫。 但首先讓我們看看現(xiàn)代數(shù)據(jù)庫(kù)系統(tǒng)中最常見(jiàn)的索引類(lèi)型。
B-Trees / B +樹(shù)
目前最常見(jiàn)的索引是B-Tree索引。 或者更確切地說(shuō),B +樹(shù)索引。
它們或者以其發(fā)明者之一Rudolf Bayer命名,或者因?yàn)樗麄冏晕移胶狻?這不是很清楚,但也并不重要。 自我平衡意味著樹(shù)木有一定的時(shí)間保證:對(duì)于B樹(shù)最有意思的是,索引的大小并不重要,對(duì)于任何相同大小的索引,時(shí)間將是相同的。 你也會(huì)看到這一點(diǎn)。
就像所有的樹(shù)(無(wú)論是在計(jì)算機(jī)科學(xué)還是在自然界中),你都以一個(gè)根開(kāi)始。 計(jì)算機(jī)科學(xué)與自然的區(qū)別在于,在計(jì)算機(jī)科學(xué)中,我們把樹(shù)的根部放在頂部。
隨你。 這是3級(jí)B +樹(shù)的根音。

等級(jí)3意味著,這個(gè)節(jié)點(diǎn)可以有3個(gè)鍵。 這就是該節(jié)點(diǎn)頂部的3個(gè)盒子的用途。 下面的4個(gè)盒子保存指向另一個(gè)節(jié)點(diǎn)的指針或數(shù)據(jù)庫(kù)表中的一行。
現(xiàn)在,假設(shè)您在此節(jié)點(diǎn)中具有密鑰11和37,并且您沒(méi)有第三個(gè)密鑰。

然后最左邊的指針指向一個(gè)節(jié)點(diǎn)的鍵小于11.第二個(gè)指針指向一個(gè)節(jié)點(diǎn),其鍵大于或等于11,小于37.第三個(gè)指針指向一個(gè)鍵大于或等于37的節(jié)點(diǎn)。
從我在開(kāi)始時(shí)顯示的表格的年齡欄索引中,可以看起來(lái)像這樣。

現(xiàn)在的魔法就是第二行節(jié)點(diǎn)中的指針。 它們中的每一個(gè)指向表中具有特定鍵的單個(gè)行,在我們的示例中,這是年齡。

但不只是這個(gè)。

您會(huì)看到每個(gè)底層節(jié)點(diǎn)中的最后一個(gè)指針如何指向下一個(gè)節(jié)點(diǎn)? 這用于“索引掃描”。 我會(huì)在稍后回來(lái)。
我們來(lái)更詳細(xì)地看看第二排節(jié)點(diǎn)。

您現(xiàn)在可以看到,來(lái)自其中一個(gè)樹(shù)節(jié)點(diǎn)的每個(gè)指針如何指向表中的單個(gè)行。
您還可以看到來(lái)自樹(shù)節(jié)點(diǎn)的這些指針如何隨機(jī)地指向表中的某些行。 這就是為什么這被稱(chēng)為“隨機(jī)訪(fǎng)問(wèn)”。 數(shù)據(jù)庫(kù)隨機(jī)在數(shù)據(jù)庫(kù)表中跳轉(zhuǎn)。
隨機(jī)訪(fǎng)問(wèn)查找
讓我們用我們之前的SQL查詢(xún)刷新我們的記憶。
SELECT * FROM people WHERE age = 19
現(xiàn)在索引如何幫助更快找到相應(yīng)的行?
那么,讓我們看看樹(shù):

它需要1步從第一個(gè)節(jié)點(diǎn)到第二個(gè)節(jié)點(diǎn)。 并且從第二個(gè)節(jié)點(diǎn)到數(shù)據(jù)庫(kù)表中的行一秒鐘。
請(qǐng)記住,我們必須查看所有7行以查看它們是否與數(shù)據(jù)庫(kù)查詢(xún)匹配。
而且由于19個(gè)指數(shù)中沒(méi)有更多的關(guān)鍵,我們就完成了。
索引掃描
現(xiàn)在,回到“索引掃描”,假設(shè)你想要計(jì)算從5歲到13歲時(shí)開(kāi)始編碼的人數(shù)。
SELECT COUNT(*) FROM people WHERE age BETWEEN 5 AND 19; SELECT COUNT(*) FROM people WHERE age >= 5 AND age <= 19;
數(shù)據(jù)庫(kù)將查找密鑰5,然后使用指向下一個(gè)節(jié)點(diǎn)的指針查找更多密鑰。

而且因?yàn)閿?shù)據(jù)庫(kù)所需的所有查詢(xún)信息都在索引中,所以數(shù)據(jù)庫(kù)根本不會(huì)查看表。
索引非常棒
讓我們讓他們?cè)贒jango。
實(shí)際上,我們已經(jīng)做到了。
有
- db_index = True ,您可以在模型字段上設(shè)置
- index_together =(('name', 'age'),)你可以在模型的Meta類(lèi)中設(shè)置
- ForeignKey() / OneToOneField()使用索引快速查找相關(guān)表中的數(shù)據(jù)
- primary_key = True ,Django自動(dòng)使用AutoField表示每個(gè)模型上的id列。
這已經(jīng)很棒了。 但是這個(gè)功能集有點(diǎn)限制。 那里不僅有B +樹(shù)索引。 還有一噸多
2016
我們來(lái)看看2016年。
馬克·坦林和我有索引的想法。 實(shí)際上,Marc在他的contrib.postgres工作中已經(jīng)有了一些想法。 我們有關(guān)于API的想法。 我們希望在Django中擁有的東西。 像,讓我們讓Django支持所有的索引 。
但是我們沒(méi)有時(shí)間去實(shí)現(xiàn)我們的想法!
但我們很幸運(yùn)。 事實(shí)上,Django項(xiàng)目很幸運(yùn)。
Google Summer of Code 2016
Django再次被接受為Google Summer of Code的組織。 謝謝Google!
對(duì)于那些不知道這是什么的人:谷歌支付學(xué)生3個(gè)月的時(shí)間在開(kāi)源項(xiàng)目上工作,同時(shí)由項(xiàng)目貢獻(xiàn)者進(jìn)行指導(dǎo)。
大多數(shù)情況下, Tim Graham ,還有Marc和我輔導(dǎo)學(xué)生Akshesh Doshi在Django中處理更通用的索引支持。 從寫(xiě)下API等提議,直到最終合并到Django中。
GSoC 2016的主要成果是django.db.models.indexes.Index(fields,name) ( docs )
它定義了所有索引的基類(lèi)。 您可以通過(guò)模型的Meta類(lèi)中的索引選項(xiàng)使用它們。
例如像這樣:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=200)
class Meta:
indexes = [
models.Index(
fields=['name'],
name='name_idx',
),
]
這將在數(shù)據(jù)庫(kù)表的名稱(chēng)列上創(chuàng)建一個(gè)B +樹(shù)索引。
當(dāng)然,這并不是什么新鮮事。 這就是在名稱(chēng)字段中使用db_index = True時(shí)可以執(zhí)行的操作。
您當(dāng)然也可以在多列上定義索引:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=200)
age = models.PositiveSmallIntegerField()
class Meta:
indexes = [
models.Index(
fields=['name', 'age'],
name='name_age_idx',
),
]
當(dāng)然,這也不是什么新東西。 你可以用index_together來(lái)完成。
但你現(xiàn)在也可以這樣做:
from django.contrib.postgres.fields import JSONField
from django.contrib.postgres.indexes import GinIndex
from django.db import models
class Doc(models.Model):
data = JSONField()
class Meta:
indexes = [
GinIndex(
fields=['data'],
name='data_gin',
),
]
定義一個(gè)GinIndex 。 這是PostgreSQL特有的。 但這是你以前無(wú)法做到的事情。 至少不可靠,沒(méi)有太多的痛苦。
GinIndex可用于索引JSON blob中的鍵值。 因此,您可以篩選表中JSONB字段中的鍵映射到特定值的表中的行。 這就像“NoSQL 1-0-1”。
Django 1.11附帶的另一種內(nèi)置索引類(lèi)型是BrinIndex ,簡(jiǎn)單地說(shuō),它可以允許更快地計(jì)算聚合。 比如,找出每篇文章最后一次購(gòu)買(mǎi)的時(shí)間。
而且由于索引是數(shù)據(jù)庫(kù)模式的一部分,顯然通過(guò)遷移來(lái)追蹤索引。 因此,當(dāng)您運(yùn)行python manage.py migrate時(shí)會(huì)創(chuàng)建索引:
BEGIN;
--
-- Create model Doc
--
CREATE TABLE "someapp_doc" (
"id" serial NOT NULL PRIMARY KEY,
"data" jsonb NOT NULL);
--
-- Create index data_gin on field(s) data of model doc
--
CREATE INDEX "data_gin" ON "someapp_doc" USING gin ("data");
COMMIT;
特色創(chuàng)意
大。
這就是昨天發(fā)布的Django 1.11。
但是Django 2.0有什么用?
什么在地平線(xiàn)上?
我們最終想要什么?
功能索引
它們?cè)诟鞣N情況下都很有用,在這種情況下,您不希望對(duì)原始值進(jìn)行索引,但可以對(duì)其進(jìn)行變化,例如字符串的小寫(xiě)。 我已經(jīng)在為此工作。 我還沒(méi)到。 我很想從理解表達(dá)式API的人那里獲得幫助。
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=200)
class Meta:
indexes = [
FuncIndex(
expression=Lower('name'),
name='name_lower_idx',
),
]
db_index = <IndexClass>
如前所述,對(duì)單個(gè)列使用索引可能非常麻煩。 因此,讓我們支持Index類(lèi)作為db_index的一個(gè)屬性。
from django.db import models class Author(models.Model): name = models.CharField( max_length=200, db_index=HashIndex )
對(duì)某些領(lǐng)域有一個(gè)B +樹(shù)是沒(méi)有意義的。 如前所示, GinIndex對(duì)于JSONField來(lái)說(shuō)非常完美。 為什么不在db_index = True時(shí)使用每個(gè)字段類(lèi)的default_index_class ?
from django.contrib.postgres.fields import JSONField from django.contrib.postgres.indexes import GinIndex from django.db import models # Somewhere in Django's JSONField implementation: # JSONField.default_index_class = GinIndex class Document(models.Model): data = JSONField(db_index=True)
重構(gòu)index_together和db_index
這個(gè)比面向用戶(hù)更引人注目:
我可以想象, db_index和index_together在內(nèi)部使用Model._meta.indexes可能是有意義的。 這是要調(diào)查的東西。
GiSTIndex
PostgreSQL中有一個(gè)GiSTIndex ,可用于地理空間查詢(xún),例如“給我所有與給定點(diǎn)最大距離為10的點(diǎn)”。 這不是在Django 1.11中。 我不知道為什么,但我猜是因?yàn)闆](méi)有人添加它。
請(qǐng)注意,Django 2.0不再支持Python 2了!
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python爬蟲(chóng)開(kāi)發(fā)之使用python爬蟲(chóng)庫(kù)requests,urllib與今日頭條搜索功能爬取搜索內(nèi)容實(shí)例
這篇文章主要介紹了python爬蟲(chóng)開(kāi)發(fā)之使用python爬蟲(chóng)庫(kù)requests,urllib與今日頭條搜索功能爬取搜索內(nèi)容實(shí)例,需要的朋友可以參考下2020-03-03
Python實(shí)現(xiàn)帶百分比的進(jìn)度條
本文給大家匯總介紹了3種使用Python實(shí)現(xiàn)帶百分比進(jìn)度條的代碼,非常的簡(jiǎn)單實(shí)用,有需要的小伙伴可以參考下2016-06-06
Python中創(chuàng)建相關(guān)系數(shù)矩陣的方法小結(jié)
相關(guān)系數(shù)矩陣是一種用于衡量變量之間關(guān)系的重要工具,本文將介紹在 Python 中創(chuàng)建相關(guān)系數(shù)矩陣的不同方法,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12
使用Python中Tkinter模塊的Treeview?組件顯示ini文件操作
這篇文章主要介紹了使用Python中Tkinter模塊的Treeview組件顯示ini文件操作,Treeview組件位于ttk模塊,該模塊自Tk8.5開(kāi)始引入,主題詳細(xì)介紹,需要的朋友可以參考一下2022-09-09
python UDP(udp)協(xié)議發(fā)送和接收的實(shí)例
今天小編就為大家分享一篇python UDP(udp)協(xié)議發(fā)送和接收的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-07-07
python 循環(huán)遍歷字典元素的簡(jiǎn)單方法
下面小編就為大家?guī)?lái)一篇python循環(huán)遍歷字典元素的簡(jiǎn)單方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-09
django1.11.1 models 數(shù)據(jù)庫(kù)同步方法
今天小編就為大家分享一篇django1.11.1 models 數(shù)據(jù)庫(kù)同步方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05

