@@ -4,9 +4,10 @@ Django ClickHouse Database Backend
4
4
简介
5
5
---
6
6
7
- ClickHouse 数据库的 Django 引擎 ,实现使用Django原生的ORM操作clickhouse数据库。
7
+ ClickHouse 数据库的 Django 数据库底端 ,实现使用Django原生的ORM操作clickhouse数据库。
8
8
9
- 底层驱动使用 [ clickhouse-driver] ( https://clickhouse-driver.readthedocs.io/en/latest/ ) ,支持django 3.2。
9
+ 底层驱动使用 [ clickhouse-driver] ( https://clickhouse-driver.readthedocs.io/en/latest/ ) ,支持django 3.2及以上。
10
+ 使用 [ clickhouse-pool] ( https://github.com/ericmccarthy7/clickhouse-pool ) 实现数据库连接池。
10
11
11
12
特性
12
13
---
@@ -31,25 +32,23 @@ ClickHouse 数据库的 Django 引擎,实现使用Django原生的ORM操作clic
31
32
### 安装
32
33
33
34
``` shell
34
- pip install git+http ://gitlab.bolean. com/wen /django-clickhouse-backend.git
35
+ pip install git+https ://github. com/jayvynl /django-clickhouse-backend
35
36
```
36
37
37
38
### 使用
38
39
39
- 示例项目可在 [ 项目 ] ( http://gitlab.bolean.com/wen/django-clickhouse-backend ) 中的 ` test_project ` 中查看。
40
+ 参考 [ 示例项目 ] ( example )
40
41
41
42
#### 数据库设置
42
43
43
- 以下例子中展示了Pg和CH双数据库的配置,其中ClickHouse中只有 ` ENGINE ` 是必选项,其他项的默认值如下:
44
+ 以下例子中展示了PostgreSQL和ClickHouse双数据库的配置,其中ClickHouse只有 ` ENGINE ` 是必选项,其他项的默认值如下:
44
45
45
46
- NAME: 默认数据库名称default
46
-
47
47
- HOST: 默认连接地址localhost
48
-
49
48
- PORT: 默认端口9000
50
-
51
- - USER PASSWORD: 默认没有用户名和密码
52
-
49
+ - USER: 默认用户default
50
+ - PASSWORD: 默认密码为空
51
+
53
52
``` python
54
53
DATABASES = {
55
54
' default' : {
@@ -62,6 +61,7 @@ pip install git+http://gitlab.bolean.com/wen/django-clickhouse-backend.git
62
61
' clickhouse_backend' : {
63
62
' ENGINE' : ' clickhouse_backend.backend' ,
64
63
' NAME' : ' default' ,
64
+ ' HOST' : ' localhost' ,
65
65
' USER' : ' DB_USER' ,
66
66
' PASSWORD' : ' DB_PASSWORD' ,
67
67
' TEST' : {
@@ -73,50 +73,10 @@ pip install git+http://gitlab.bolean.com/wen/django-clickhouse-backend.git
73
73
74
74
#### 定义数据库模型
75
75
76
- 下面的例子中定义了ENGINE 、ORDER BY、PARTITION BY等ClickHouse常用的属性。
76
+ 支持ENGINE 、ORDER BY、PARTITION BY等ClickHouse常用的属性,参考 [ 示例项目 ] ( example/testapp/models.py ) 。
77
77
78
- ` GenericIPAddressField ` 建议使用 ` clickhouse .models.fields.GenericIPAddressField` ,否则修改带IP类型的对象在保存时会报错。
78
+ ` GenericIPAddressField ` 建议使用 ` clickhouse_backend .models.fields.GenericIPAddressField` ,否则修改带IP类型的对象在保存时会报错。
79
79
80
- ``` python
81
- from django.db import models
82
-
83
- from clickhouse_backend.models import ClickhouseModel
84
- from clickhouse_backend.models.engines import ReplacingMergeTree
85
- from clickhouse_backend.models.indexes import Index, Set
86
- from clickhouse_backend.models.fields import GenericIPAddressField
87
-
88
-
89
- class Packet (ClickhouseModel ):
90
- device = models.IntegerField(default = 0 )
91
- src_ip = GenericIPAddressField(' 源IP地址' , default = ' ::' )
92
- src_mac = models.CharField(' 源MAC地址' , max_length = 17 , default = ' ' )
93
- src_port = models.PositiveSmallIntegerField(' 源端口' , default = 0 )
94
- dst_ip = GenericIPAddressField(' 目的IP地址' , default = ' ::' )
95
- dst_mac = models.CharField(' 目的MAC地址' , max_length = 17 , default = ' ' )
96
- dst_port = models.PositiveSmallIntegerField(' 目的端口' , default = 0 )
97
- transport = models.CharField(' 传输层协议' , max_length = 3 , default = ' ' )
98
- protocol = models.TextField(' 协议' , default = ' ' )
99
- content = models.TextField(' 全部应用层内容' , default = ' ' )
100
- occurred_at = models.DateTimeField(' 发生时间' )
101
- created_at = models.DateTimeField(' 创建时间' , auto_now_add = True )
102
- length = models.PositiveIntegerField(' 字节数' )
103
- count = models.PositiveIntegerField(' 包数' , default = 1 )
104
-
105
- class Meta :
106
- verbose_name = ' 协议审计'
107
- ordering = [' -id' ]
108
- db_table = ' packet'
109
- engine = ReplacingMergeTree()
110
- order_by = (' id' ,)
111
- partition_by = Function(' occurred_at' , function = ' toYYYYMMDD' )
112
- indexes = [
113
- Index(
114
- fields = (' src_ip' , ' dst_ip' ),
115
- type = Set(1000 ),
116
- granularity = 4
117
- )
118
- ]
119
- ```
120
80
121
81
#### 多数据库配置
122
82
@@ -130,18 +90,18 @@ class Packet(ClickhouseModel):
130
90
131
91
def db_for_read (self , model , ** hints ):
132
92
if model._meta.app_label in self .route_app_labels:
133
- return ' clickhouse_backend '
93
+ return ' clickhouse '
134
94
return None
135
95
136
96
def db_for_write (self , model , ** hints ):
137
97
if model._meta.app_label in self .route_app_labels:
138
- return ' clickhouse_backend '
98
+ return ' clickhouse '
139
99
return None
140
100
141
101
def allow_migrate (self , db , app_label , model_name = None , ** hints ):
142
102
if app_label in self .route_app_labels:
143
- return db == ' clickhouse_backend '
144
- elif db == ' clickhouse_backend ' :
103
+ return db == ' clickhouse '
104
+ elif db == ' clickhouse ' :
145
105
return False
146
106
return None
147
107
```
@@ -156,26 +116,30 @@ class Packet(ClickhouseModel):
156
116
157
117
``` shell
158
118
python manage.py makemigrations
159
- python manage.py migrate --database clickhouse_backend
119
+ python manage.py migrate --database clickhouse
160
120
```
161
121
162
- 注意迁移的时候必须指定 --database clickhouse 指定执行clickhouse的迁移,否则默认执行default数据库的迁移。
122
+ 注意迁移的时候必须指定 ` --database clickhouse ` 指定执行clickhouse的迁移,否则默认执行default数据库的迁移。
163
123
164
124
#### 单元测试
165
125
166
126
如果使用django内置的TestCase,需要为测试的类增加一个databases类属性,指定在测试中使用clickhouse数据库,例如:
167
127
``` python
128
+ from django.test import TestCase
168
129
class Test (TestCase ):
169
130
databases = {' default' , ' clickhouse_backend' }
170
131
```
171
132
172
133
如果使用pytest和pytest-django,[ 多数据库测试] ( https://pytest-django.readthedocs.io/en/latest/database.html#tests-requiring-multiple-databases ) 都需要为pytest.mark.django_db增加一个databases参数,指定在测试中使用clickhouse数据库。
173
134
174
135
``` python
136
+ import pytest
137
+ from testapp.models import Event
138
+
175
139
@pytest.mark.django_db (databases = {' default' , ' clickhouse_backend' })
176
140
class Test :
177
141
def test_spam (self ):
178
- assert MyModel .objects.using(' clickhouse_backend ' ).count() == 0
142
+ assert Event .objects.using(' clickhouse ' ).count() == 0
179
143
```
180
144
181
145
> ** 注意**
@@ -188,7 +152,7 @@ class Packet(ClickhouseModel):
188
152
189
153
### 数据库迁移
190
154
191
- 数据库迁移只支持初始的创建 、表名修改、字段增删改等简单修改,那么如何维护其他的表结构更新呢?
155
+ 数据库迁移只支持表初始的创建 、表名修改、字段增删改等简单修改,那么如何维护其他的表结构更新呢?
192
156
193
157
目前没有好的办,假如没有生成正确的迁移文件,那么就需要手写迁移文件。
194
158
@@ -204,13 +168,13 @@ Django自动生成的主键是BigAutoField或AutoField,因为Django内置的mo
204
168
205
169
- 使用BigAutoField
206
170
207
- 在数据库中生成Int64类型的列,插入数据时如果未指定主键值,那么默认会使用IdWorker生成唯一主键 。
171
+ 在数据库中生成Int64类型的列,插入数据时如果未指定主键值,那么默认会使用 ` clickhouse.idworker.id_worker ` 生成唯一主键 。
208
172
209
- 项目中用到了Twitter的snowflake算法生成唯一主键,实现在clickhouse. snowflake.IdWorker ,如果使用多进程启动项目,必须保证每个项目的worker_id是唯一的 。
173
+ 项目中用到了Twitter的snowflake算法生成唯一主键,实现在 ` clickhouse.idworker. snowflake.SnowflakeIDWorker ` ,如果使用多进程启动项目,必须保证每个进程的(CLICKHOUSE_WORKER_ID, CLICKHOUSE_DATACENTER_ID)环境变量是唯一的 。
210
174
211
- 可以通过CLICKHOUSE_SNOWFLAKE_ID环境变量传入worker_id。因为使用5位表示workerID ,所以范围是 0-31,如果传入了无效的worker_id会报错 。
212
-
213
- 如果没有指定CLICKHOUSE_SNOWFLAKE_ID,默认使用31。最多可以为32个进程指定不同的worker id 。
175
+ 因为使用work_id和datacenter_id都是5bit ,所以范围是 0-31,如果传入了无效的值会报错,CLICKHOUSE_WORKER_ID默认值是0,CLICKHOUSE_DATACENTER_ID如果未提供则随即生成 。
176
+
177
+ 出于效率,默认的 ` clickhouse.idworker.snowflake.SnowflakeIDWorker ` 是线程不安全的,用户可继承 ` clickhouse.idworker.base.BaseIDWorker ` 实现自己的版本,并在项目的 ` settings.py ` 中设置 ` CLICKHOUSE_ID_WORKER ` 值为自定义 ` BaseIDWorker ` 实例的倒入路径 。
214
178
215
179
在Django 3.12,django内置的app或其他第三方app如果要使用BigAutoField作为默认主键类型,需要设置:
216
180
@@ -225,7 +189,8 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
225
189
例如通过设置 ` mutations_sync=1 ` 能够让数据的更新同步执行,默认情况下数据的修改是在后台异步执行的,参考 [ mutations_sync] ( https://clickhouse.com/docs/en/operations/settings/settings/#mutations_sync ) :
226
190
227
191
``` python
228
- Packet.objects.filter(protocol = ' S7' ).setting(mutations_sync = 1 ).update(dst_port = 102 )
192
+ from testapp.models import Event
193
+ Event.objects.filter(protocol = ' S7' ).setting(mutations_sync = 1 ).update(dst_port = 102 )
229
194
```
230
195
231
196
待实现
0 commit comments