0%

在Django中无法实现Expression索引,至少正常途径是做不到的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# django.db.models.indexes.Index
class Index:
def create_sql(self, model, schema_editor, using='', **kwargs):
fields = [model._meta.get_field(field_name) for field_name, _ in self.fields_orders]
col_suffixes = [order[1] for order in self.fields_orders]
condition = self._get_condition_sql(model, schema_editor)
return schema_editor._create_index_sql(
model, fields, name=self.name, using=using, db_tablespace=self.db_tablespace,
col_suffixes=col_suffixes, opclasses=self.opclasses, condition=condition,
**kwargs,
)

# django.db.backends.base.schema.BaseDatabaseSchemaEditor
class BaseDatabaseSchemaEditor:
def _create_index_sql(self, model, fields, *, name=None, suffix='', using='',
db_tablespace=None, col_suffixes=(), sql=None, opclasses=(),
condition=None):
"""
Return the SQL statement to create the index for one or several fields.
`sql` can be specified if the syntax differs from the standard (GIS
indexes, ...).
"""
tablespace_sql = self._get_index_tablespace_sql(model, fields, db_tablespace=db_tablespace)
columns = [field.column for field in fields]
sql_create_index = sql or self.sql_create_index
table = model._meta.db_table

def create_index_name(*args, **kwargs):
nonlocal name
if name is None:
name = self._create_index_name(*args, **kwargs)
return self.quote_name(name)

return Statement(
sql_create_index,
table=Table(table, self.quote_name),
name=IndexName(table, columns, suffix, create_index_name),
using=using,
columns=self._index_columns(table, columns, col_suffixes, opclasses),
extra=tablespace_sql,
condition=self._index_condition_sql(condition),
)
Read more »

First, generate a new empty migration:

1
2
3
$ python manage.py makemigrations app --empty --name add_index_runsql
Migrations for 'app':
app/migrations/0002_add_index_runsql.py

Next, edit the migration file and add a RunSQL operation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# migrations/0002_add_index_runsql.py

from django.db import migrations, models

class Migration(migrations.Migration):
atomic = False

dependencies = [
('app', '0001_initial'),
]

operations = [
migrations.RunSQL(
'CREATE INDEX app_sale_sold_at_b9438ae4 ON app_sale (sold_at);',
reverse_sql='DROP INDEX "app_sale_sold_at_b9438ae4";',
),
]
Read more »

使用db_index参数创建btree索引,当字段类型是varchartext时会额外创建like index, 具体参见:DatabaseSchemaEditor._create_like_index_sql

1
2
class BTreeIdx(models.Model):
content = models.CharField(max_length=200, db_index=True)
1
2
3
4
5
6
7
8
9
CREATE INDEX btree_idx_btreeidx_content_511ce749
ON public.btree_idx_btreeidx USING btree
(content COLLATE pg_catalog."default" ASC NULLS LAST)
TABLESPACE pg_default;

CREATE INDEX btree_idx_btreeidx_content_511ce749_like
ON public.btree_idx_btreeidx USING btree
(content COLLATE pg_catalog."default" varchar_pattern_ops ASC NULLS LAST)
TABLESPACE pg_default;
Read more »

使用db_index参数创建索引只会创建btree索引:

1
2
class JsonIdx(models.Model):
content = models.JSONField(db_index=True)
1
2
3
4
CREATE INDEX json_idx_jsonidx_content_d01e171b
ON public.json_idx_jsonidx USING btree
(content ASC NULLS LAST)
TABLESPACE pg_default;
Read more »

Name Indexable Operators Comments
jsonb_ops ? ?& ?| @> @? @@ is default
jsonb_path_ops @> @? @@ 支持少量的操作,但能提供更好的表现
1
2
3
4
5
6
7
8
9
10
11
12
-- drop table if exists test;
create table if not exists test (id int, js jsonb);

create index if not exists test_js_idx on test using gin (js jsonb_path_ops);

-- 插入1000w条数据花了近1个小时
insert into test (id, js)
select n, row_to_json(row(uuid_generate_v1(), uuid_generate_v1(), uuid_generate_v1(), uuid_generate_v1(), uuid_generate_v1()))
from generate_series(1, 10000000);

-- 查询时间在100ms~130ms之间
select * from test where js @> jsonb '{"f2": "5e36a48c-86f3-11eb-b9d7-0242ac120005"}';

Odoo是基于Python写的一系列开源商业ERP系统,前身是OpenERP。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
version: '3.7'

services:
odoo:
image: odoo
restart: always
labels:
- traefik.http.routers.odoo.rule=Host(`odoo.yourdomain.com`)
- traefik.http.routers.odoo.entrypoints=websecure
- traefik.http.routers.odoo.service=odoo
- traefik.http.services.odoo.loadbalancer.server.port=8069
environment:
- HOST=postgres
- PORT=5432
- USER=odoo
- PASSWORD=odoo
volumes:
- data:/var/lib/odoo
- addons:/mnt/extra-addons

volumes:
data:
addons:

值得注意的是,服务初始化的时候无需创建数据库,它会在后续的初始化过程中自动创建。

XRay是一款原生支持XTLS黑科技且源自V2Ray却超越V2Ray的科学上网工具。

服务部署

docker-compose.yml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
version: '3.7'

services:
xray:
image: teddysun/xray
restart: always
labels:
- traefik.http.routers.xray.rule=Host(`xray.yourdomain.com`) && Path(`/vxknSD77hYanwwRM`)
- traefik.http.routers.xray.entrypoints=websecure
- traefik.http.routers.xray.service=xray
- traefik.http.services.xray.loadbalancer.server.port=9000
volume:
- ./config.json:/etc/xray/config.json
Read more »

SonarQube是一个开源的代码质量管理平台,专用于持续集成分析和测量技术质量,从项目的组合到方法。

部署SonarQube

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
version: '3.7'

services:
sonarqube:
image: sonarqube
restart: always
labels:
- traefik.http.routers.sonarqube.rule=Host(`sonarqube.yourdomain.com`)
- traefik.http.routers.sonarqube.entrypoints=websecure
- traefik.http.routers.sonarqube.service=sonarqube
- traefik.http.services.sonarqube.loadbalancer.server.port=9000
environment:
- SONAR_JDBC_URL=jdbc:postgresql://postgres/sonarqube
- SONAR_JDBC_USERNAME=postgres
- SONAR_JDBC_PASSWORD=postgres
volumes:
- extensions:/opt/sonarqube/extensions

volumes:
extensions:
Read more »

关于Traefik,参考我的另一篇文章:使用Docker部署Traefik服务

本文使用alidns,需要先申请ALICLOUD_ACCESS_KEYALICLOUD_SECRET_KEY

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
version: '3.7'

services:
traefik:
image: traefik
restart: always
labels:
- traefik.http.routers.traefik.rule=Host(`traefik.yourdomain.com`)
- traefik.http.routers.traefik.entrypoints=websecure
- traefik.http.routers.traefik.service=traefik
- traefik.http.services.traefik.loadbalancer.server.port=8080
command:
- --providers.docker
- --api.dashboard=true
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.websecure.address=:443
- --entrypoints.websecure.http.tls.certResolver=letsencrypt
- --certificatesResolvers.letsencrypt.acme.storage=/certs/acme.json
- --certificatesResolvers.letsencrypt.acme.dnsChallenge.provider=alidns
environment:
- ALICLOUD_ACCESS_KEY=xxx
- ALICLOUD_SECRET_KEY=xxx
network_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- certs:/certs

volumes:
certs:

更多provider参考:https://doc.traefik.io/traefik/https/acme/#providers

在做本地开发的时候,免不了需要模拟https环境,这时候便需要使用自签名证书,自签名证书可以使用openssl生成, 但这一系列步骤过于复杂 – 我们需要使用一种简单且友好的方式生成本地https证书,那便是mkcert方案。

Installation

MacOS

1
brew install mkcert

Linux

1
2
3
sudo apt install libnss3-tools
# or
sudo yum install nss-tools
Read more »