主页

clickhouse集群配置

2021-03-17
数据库

  • 查看集群select * from system.clusters
  • 分片shard是指包含数据不同部分的服务器。要读取所有数据,必须访问所有分片。
  • 副本replica是存储复制数据的服务器,要读取所有数据,访问一个切片上的任意副本上的数据即可。

集群部署 #

  1. 在群集的所有机器上安装clickhouse服务端

  2. 在配置文件中设置群集配置

    <remote_servers>
    	<perftest_1shards_1replicas>
        	<shard>
            	<replica>
                	<host>localhost</host>
                    <port>9000</port>
                </replica>
            </shard>
        </perftest_1shards_1replicas>
    </remote_servers>
    
  3. 在集群上创建本地表

  4. 在集群上创建分布式表

    • 分布式表实际上是一种视图(view),从分布式表中执行select查询会使用集群所有分片的资源。
  • internal_replication
    • 当参数为true时,写操作只选择一个正常的副本写入数据。如果表是复制表Replicated*Merge,请使用此方案。
    • 当为false时,写操作会将数据写入所有副本。因为不会检查副本一致性,随着时间的推移,副本数据可能不一样。

数据副本 #

  • 对于INSERT和ALTER语句操作数据时会在压缩的情况下被复制。
  • CREATE、DROP、ATTACH、DETACH和RENAME语句只会在单个服务器上执行,不会被复制。
    • 可复制表的不同副本可以有不同的名称
  • 如果配置文件中没有设置ZooKeeper,则无法创建复制表,并且现在任何现有的复制表都将变为只读。

创建复制表 #

CREATE TABLE table_name
(
	EventDate DateTime,
    CounterID UInt32,
    UserID UInt32,
    Ver UInt16
)ENGINE = ReplicatedReplacingMergeTree(zoo_path, replica_name, other_parameters)
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID)

django起步

2021-03-16
python

起步 #

  • django项目目录
mysite/
    manage.py #管理django项目的命令行工具
    mysite/	#python包,名字就是当引用它内部任何东西时需要用到的python包名
        __init__.py	#告诉python这个目录应该被认为是一个python包
        settings.py	#项目的配置文件
        urls.py #url声明
        asgi.py #运行在asgi兼容的web服务器上的入口
        wsgi.py #运行在wsgi兼容的web服务器上的入口
  • 运行项目的命令python manage.py runserver
  • 创建一个应用python manage.py startapp polls
  • 应用urls.py中使用到的函数path()有四个参数,两个必须参数route和view,两个可选参数kwargs和name
    • route是一个匹配URL的准则,当响应一个请求时,他会从urlpatterns的第一项开始,按顺序依次匹配列表中的项。这些准则不会匹配get和post参数或域名
    • view:当找到了一个匹配的准则,就会调用这个特定的视图函数,并传入一个HttpRequest对象作为第一个参数,被捕获的参数以关键字参数的形式传入。
    • kwages:任意个关键字参数可作为一个字典传递给目标视图函数。
    • name:为url取名
  • 函数include()允许引用其他URLconfs

模型 #

设置mysite/sittings.py #

  • 设置时区TIME_ZONE = 'Asia/Shanghai' USE_TZ = True
    • 当启用了时区支持(即USE_TZ = True),Django将在数据库中以UTC存储日期信息,而在模板和表单中转化为最终用户的时区
  • 设置数据库
  • INSTALLED_APPS是在项目中启用的Django应用,这里面的某些应用需要数据表,可以用python manage.py migrate生成

创建模型 #

  • 模型设真实数据的描述,它包含了存储的数据所必要的字段和行为。
  • Django的迁移代码是从模型文件中自动生成的
  • 每个模型都是django.db.models.Model类的子类,每个模型有许多的类变量,他们都表示一个数据库的字段,每个字段都是Field类的实例,每个Field类实例变量的名字也是数据库的列名。
#polls/models.py
from django.db import models

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

激活和修改模型 #

  • 首先把应用安装到项目中,在INSTALLED_APPS中添加polls.apps.PollsConfig
  • 执行python manage.py makemigrations polls
  • 迁移数据存放在应用的migrations/0001_initial.py中,可以通过python manage.py sqlmigrate polls 0001来查看对应的sql
  • 运行python manage.py migtate来应用数据库迁移
  • 通过python manage.py shell可进入当前项目对应的命令行
  • 给模型增加__str__方法能带来一些方便

Django管理页面 #

  • 首先创建一个能登陆管理界面的用户python manage.py createsuperuser
  • 将创建的models注册,以便后台可以管理
#polls/admin.py
from django.contrib import admin
from .models import Question

admin.site.register(Question)
  • 运行项目python manage.py runserver

视图view #

  • 向应用的views.py中添加视图函数,这些函数的第一个参数是request,也以有第二、第三个参数。每个视图必须做的只有返回一个包含请求页面内容的HttpResponse对象或者抛出一个异常
def detail(request, question_id):
    context = {
        'question_id': question_id,
    }
    #template = loader.get_template('xxx/xxxx.html')
    #return HttpResponse(template.render(context, request))
    return render(request, 'xxx/xxxx.html', context)
  • 当使用模型的get函数获取对象,如果不存在,可以抛出Http404错误,Django提供了一个快捷函数get_object_or_404

  • 将这些视图添加加到应用的urls.py中

#为url名称添加命名空间
app_name = 'polls'
urlpatterns = [
    #...
    #int:是一个转换器,决定了应该以什么变量类型匹配这部分url路径
    path('<int:question_id>/detail/', views.detail, name='detail'),
]
  • 当其他人访问网站的某个页面时,比如’polls/34/detail’,django将会载入项目的urls.py模块,因为这在ROOT_URLCONF中配置了,然后django寻找名为urlpatterns变量并按顺序匹配正则表达式,在找到匹配项’polls/’,它切掉了匹配的文本’polls/’,将剩余文本'34/detail’发送至’polls.urls’中做进一步处理

模板 #

  • 在应用路径下创建templates目录,项目的TEMPLATES配置项描述了django如何载入和渲染模板,默认将APP_DIRS设置为True,这一选项让DjangoTemplates能在每个INSTALLED_APPS文件夹中寻找templates子目录。
  • 由于django能直接访问到templates文件夹,为了放置出现模板文件名冲突,最好的方法就是将它们放到一个与自身应用重名的子文件夹里
  • 指向具有命名空间的视图
<a href="{% url 'polls:detail' question.id %}">{{ context.question_id }}</a>
  • 无论何时,当需要创建一个改变服务器端数据的表单时,请使用post。
  • django自带了一个非常有用的防御系统,所有针对内部url的post表单都应该使用{% csrf_token %}模板标签。
  • request.POST是一个字典类型对象,可以通过关键字来获取提交的数据,获取的值永远是字符串。如果key提供错误,将会抛出一个KeyError。
  • 函数reverse()能避免在视图函数中硬编码url,他需要我们给出想要跳转的视图的名字和该视图所对应的url模式中需要给该视图提供的参数。

python正则表达式

2021-03-15
python

  • \d匹配一个数字
  • \w匹配一个数字或字母
  • .匹配任意字符
  • *匹配0到多个字符,+匹配1到多个字符,?匹配0或1个字符,{n}表示n个字符,{n,m}表示n-m个字符
  • \s匹配一个空格(也包括tab)
\d{3}\s+\d*	#匹配3个数字,后面至少有一个空格,再后面有可能有数字,有可能没数字
  • []可用于更精确的匹配
[0-9a-z\_] #匹配一个数字或一个小写字母或一个下划线
  • A|B可匹配A或B
  • ^表示行的开头,^\d表示必须以数字开头
  • $表示行的结束,$\d表示必须以数字结束

re模块 #

  • 因为python字符串本身也用\转义,可以使用r前缀,就可以不用考虑字符串转义了。r'ABC\-001'
  • re.match(r'\d{3}\-\d$'),'010-1'),如果匹配成功就返回一个Mache对象,否则返回none。
  • re.split(r'[\s\,]+','a,b, c d'),切割字符串,能切割出['a','b','c']
  • 正则表达式还有提取字串的功能,用()表示的就是要提取的分组。r'(\d{3})-(\d{3,8})'就定义了两个组,可以使用group(n)方法提取出子串

贪婪匹配 #

  • 正则默认的时贪婪匹配,加个?就可以采用非贪婪匹配。\d+?
r = re.match('(\d+?)(0*)$','1200000')
print(r.groups())
#结果('12', '00000')

python模块

2021-03-15
python

datetime #

from datetime import datetime,timedelta
  • 获取当前时间datetime.now()
  • 指定的某个日期和时间datetime(2021,3,15,19,48)
  • 将datetime类型转化为timestamp,(timestamp是一个浮点数,整数位表示秒) datetime(2021,3,15,19,49).timestamp()
  • timestamp转化为datetime datetime.fromtimestamp(1615809156.889315)
  • str转换为datetime datetime.strptime('2021-3-15 19:55:59','%Y-%m-%d %H:%M:%S')
  • datetime转str datetime.now().strftime('%a,%b %d %H:%M')
  • datetime加减 datetime.now() + timedelta(days = 1, hours = 10)
  • 时区转换
utc_dt = datetime.utcnow()
utc_dt.astimezone(timezone(timedelta(hours=8)))

collections #

  • namedtuple:是一个函数,可以用来创建一个自定义的tuple对象,同时规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素。
from collections import namedtuple
Point = nametuple('Point',['x', 'y'])
p = Point(1, 2)
  • deque:双向列表,方法有append()、pop()、appendleft()、popleft()
  • defaultdict:和dict类似,但是在key不存在的时候返回一个默认值。
  • OrderedDict:key会按照插入的顺序排序
  • ChainMap:可以把一组dict串起来组成一个逻辑上的dict,本身也是一个dict,但在查找的时候,会按照顺序在内部的dict依次查找。
  • Counter是一个计数器,可以统计字符出现的个数,实际上也是dict的子类

base64 #

  • base64是一种用64个字符表示任意二进制数据的方法,64个字符指的是A-Z, a-z, 0-9, +, /。实现方法是将3个字节(3x8bit)的二进制数据编码为4字节(4x6bit,6bit的数据刚好能用上面这64个字符表示)的数据。6bit的数据刚好能用这64个字符表示。
  • 编码base64.b64encode(b'binary');解码base64.b64decode(b'binary')

hashlib #

  • 摘要算法又称为哈希算法,散列算法,它通过一个函数,把任意长度的数据转化为一个固定长度的数据串。它是一个单向函数,对原始数据做一点点的修改都会导致计算出的摘要完全不同。
import hashlib
md5 = hashlib.md5()
#也可以将串分开,多次调用update方法
md5.update("xiaoxiang.space".encode("utf-8"))
#md5结果是128bit,通常用32位的16进制表示
print(md5.hexdigest())

sha1 = hashlib.sha1()
sha1.update("xiaoxiang.space".encode("utf-8"))
#sha1的结果是160bit,通常用一个40位的16进制字符串表示
print(sha1.hexdigest())

itertools #

  • 无限迭代器itertools.count()
  • cycle()会把传入的一个序列无限重复下去itertools.cycle('ABC')
  • repeat()可以把一个元素无限重复下去,提供了第二个参数可以限定重复次数itertools.repeat('A', 3)
  • 可通过takewhile()根据函数条件来截取一个有限的序列。
  • chain()可以把一组迭代对象串联起来,形成一个更大的迭代器。itertools.chain('ABC', 'XYZ')
  • groupby()可以把迭代器中相邻的重复元素挑出来放在一起。挑选规则是通过函数完成的,只要作用于函数的两个元素返回的值相等,这两个元素就被认为是一组的,函数的返回值作为组的key。

contextlib #

  • 任何对象,只要正确实现了上下文管理,就可以用于with语句。实现上下文管理是通过__enter____exit__这两个方法实现的。可以通过contextlib标准库中的@contextmanager来实现上下文管理(更简单)
  • 有时候,我们希望在某段代码执行前后自动执行特定代码,也可以通过@contextmanager实现
import contextlib
@contextlib.contextmanager
def tag(name):
    print("<%s>"%name)
    yield
    print("</%s>"%name)

with tag("h1"):
    print("hello")
    print("world")
  • 如果一个对象没有实现上下文,就不能把他用于with语句,可以用closing()来将该对象变成上下文。

python异常-IO-进程等

2021-03-15
python

错误 #

try:
    i = 10 / int('a')
except ValueError as e:
    print("ValueError",e)
except ZeroDivisionError as e:
    print("ZeroDivisionError",e)
    #raise如果不带参数,就会把当前错误原样抛出
    raise 
finally:
    print("END")

单元测试要用到了再回来补充: 单元测试

IO #

  • 以读的方式打开一个文件对象f = open('test.txt','r')

  • 文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源。可以使用with open('test.txt','r') as f:,这种形式会自动帮我们调用close()方法。

  • 读取二进制文件open('test','rb'),读取非UTF-8编码的文件open('gbk.txt', 'r', encoding = 'gbk'),遇到编码错误后忽略open('test', 'r', encoding = 'gbk', errors = 'ignore')

  • 写文件就是将r参数改为w参数,当在写文件时,操作系统往往不会立刻把数据写入磁盘,而是先缓存起来,只有调用close方法时才会保证把没有写入的数据全部写入磁盘。

  • StringIO和BytesIO是在内存中读写数据。

  • 序列化

python面向对象

2021-03-14
python

  • python中所有数据类型都可视为对象。
  • 所有的类都会继承object类。
  • 类中定义函数的第一个参数是self,调用时不用传递该参数。
  • 属性前面加__(两个下划线)就变成了一个私有属性。双下划线开头,双下划线结尾的是特殊变量,特殊变量是可以直接访问的,不是private变量。一个下划线开头的变量名是可以被外部访问的,但是按约定俗称的规定,请把它视为私有变量。
  • 双下划线开头的属性不能被外部访问是因为这个属性被改名字了。
  • 判断一个变量是否是某个类型可以用isinstance()判断,判断对象类型可以使用type方法。
  • 使用dir()可获得对象的所有属性和方法。
  • 自己写的类如果也想用len()方法的话,可以自己写一个__len__方法。实际上,当我们使用len()方法的时候,它会自动去调用该对象的__len__()方法。
  • __str__相当于java中的toString,还有一个__repr__是返回开发者看到的字符串。
  • __getitem__可按下标访问数据的项。
  • 任何类只要定义一个__call__方法,就可以对实例进行调用。

类的定义 #

class Student(object):
    #类属性
    name = 'Student'
    def __init__(self, name):
        #实例属性
        self.name = name
#编写程序不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性        

动态绑定 #

  • 当创建了一个对象后,我们可以给该对象绑定任何属性和方法,但是该属性和方法只在当前对象上有效。为了给所有实例都绑定方法,可以给class绑定方法。
class Student(object):
    #可以用来限制class实例能添加的属性,仅对当前类实例起作用,对继承的子类不起作用
    #除非在子类中也定义__slots__,这样子类实例允许定义的属性就是自身的__slots__加上父类的__slots__
    __slots__ = ('name', 'age')
def set_age(self, age)
    self.age = age
s = Student()
#给对象动态绑定了一个属性
s.name = 'xiaoxiang'
#给对象动态绑定一个方法,只在该对象上有效
s.set_age = MethodType(set_age, s)
#给类动态绑定一个方法
Student.set_age = set_age

getter和setter #

  • 使用@property代替getter,以及创建setter的方法见下面的代码
class Student:

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name

student = Student()
student.name = 'xiaoxiang'
print(student.name)

__iter____next__ #

  • 如果一个类想被用于for-in,就必须实现__iter____next__方法
class Fib:
    def __init__(self):
        self.a = 0
        self.b = 1

    def __iter__(self):
        #实例自身就是迭代对象
        return self

    def __next__(self):
        self.a, self.b = self.b, self.b + self.a
        if self.a > 100:
            raise StopIteration()
        #返回下一个值
        return self.a

for i in Fib():
    print(i)

__getattr__ #

  • __getattr__用于动态返回一个属性,只在没有找到属性的情况下才会调用该方法。
class Url:

    def __init__(self, item = ''):
        self.item = item

    def __getattr__(self, item):
        return Url('%s/%s'%(self.item, item))

    def __str__(self):
        return self.item

    __repr__ = __str__

print(Url().xiaoxiang.ayu.liuchan.list)
#结果:/xiaoxiang/ayu/liuchan/list

枚举 #

from enum import Enum, unique

#该注解用于保证没有重复值
@unique
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6

type和metaclass #

  • class(类)的类型就是type,对象的类型就是类名
  • 可以通过type()函数依次传入3个参数来创建一个class对象:①class的名称;②继承的父类集合;③class的方法名称与函数绑定。type('hello', (object, ), dict(hello = 'hello world'))
  • 通过type()函数创建的类和直接写class是完全一样的,因为python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。
  • 可以把类看作是metaclass(元类)创建出来的实例。
  • metaclass后面用到了再来看吧,链接在这-> metaclass

clickhouse-statement

2021-03-11
数据库

  • 可以在system.data_type_families中检查某个数据类型的名称是否大小写敏感,case_insensitive=1表示大小写不敏感。看完后我建议数据类型的命名首字母大写,并采用驼峰命名法。
  • 除了标准sql的关键字和许多其他数据库实现的一些关键字,clickhouse其他的关键字都是大小写敏感的!所以如果你发现语句看上去没错,执行就是有问题,看看是不是大小写出问题了。
  • 关键字不是保留的,它们仅在相应的上下文中才会被处理。
  • 变量名可以使用反引号包含起来。
  • 别名在查询和子查询中是全局可见的。

select #

  • select子句是在 from、where、group by等所有操作完成后计算的,如果select子句中包含聚集函数,clickhouse会在执行group by期间处理这些聚集函数

  • 如果要用re2正则表达式匹配列,可以使用COLUMNS(‘xxx’),这样可以一次匹配多个列(对于是否常用我持否定态度)。

  • 当FROM被省略时,数据从system.one表中读取

  • ARRAY JOIN

    • 对于包含数组列的表来说是一种常见的操作,用于生成一个新表(新表有一列将包含数组中的每一个元素,其他列可能会出现元素重复)。
  • DISTINCT

    • 不支持select包含有数组的列
    • 当ORDER BY 被省略且LIMIT被定义时,在读取所需数量的不同行后立即停止运行。
    • DISTINCT在ORDER BY之前执行

alter #

  • 仅支持MergeTree家族,Merge以及Distributed等引擎表。
  • alter操作会阻塞对表的所有读写操作。

列操作 #

  • 增加列
    • ADD COLUMN [IF NOT EXISTS] name [type] [default_expr] [codec] [AFTER name_after]
    • 使用指定的name、type、codec以及default_expr往表中增加新的列。
    • 如果sql中包含IF NOT EXISTS,执行语句如果已存在,则clickhouse不会报错,不能将新的列添加到表的开始位置,当指定了AFTER name_after,则会将新的列添加到指定列的后面。
    • 添加列仅仅是改变原有表的结构,不会对已有数据产生影响。
  • 删除列
    • DROP COLUMN [IF EXISTS] name
  • 清空列
    • CLEAR COLUMN [IF EXISTS] name IN PARTITION partition_name
    • 重置指定分区中的值。
  • 增加注释
    • COMMENT COLUMN [IF EXISTS] name 'comment'
    • 每个列都可以包含注释,注释信息在DESCRIBE table中查看
  • 改变列的值类型,默认表达式,TTL
    • MODIFY COLUMN [IF EXISTS] name [type] [default_expr] [TTL]
    • 当改变列的类型时,列的值也被转换了,如同对列使用toType函数一样,执行起来要花费很长时间。

clickhouse起步

2021-03-11
数据库

  • online analytical processing of queries(OLAP):联机分析
  • clickhouse是一个用于OLAP的列式数据库管理系统(来自同一列的数据被存储在一起)。
  • sudo service clickhouse-server start启动clickhouse.
  • clickhouse不要求主键唯一

clickhouse连接客户端 #

clickhouse-client #

  • clickhouse-client命令行参数
  • clickhouse-client配置文件地址(使用以下第一个配置文件)
    • 通过--config-file参数指定
    • ./clickhouse-client.xml
    • ~/.clickhouse-client/config.xml
    • /etc/clickhouse-server/config.xml

mysql #

  • 可以通过mysql命令工具连接,需要在配置文件中配置mysql_port:<mysql_port>9004</mysql_port>
  • 使用命令行工具mysql进行连接:mysql --protocol tcp -u default -P 9004

数据库引擎 #

mysql引擎 #

  • 用于将远程的mysql服务器中的表映射到clickhouse中,并允许对表进行insert和select查询等,但无法执行rename、create table、alter操作。

    CREATE DATABASE [IF NOT EXISTS] db_name [ON CLUSTER cluster] ENGINE = MYSQL('host:port',['database'| database],'user','password')
    
  • 类型的对应,其他的mysql数据类型全部转化为字符串

表引擎 #

merge tree #

  • MergeTree:设计用于插入大量数据当一张表当中。数据可以以数据片段的形式一个接一个的快速写入,数据片段在后台按照一定的规则进行合并。

  • SummingMergeTree:合并SummingMergeTree表的数据片断时,clickhouse会把所有具有相同主键的行合并为一行,该行包含了被合并的行中具有数值数据类型的列的汇总值。如果主键的组合方式使得单个键值对应于大量的行,则可以显著的减少存储空间并加快数据查询的速度。

  • ReplacingMergeTree:该引擎会删除排序键值相同的重复项。适用于在后台清除重复的数据以节省空间,但不保证没有重复数据出现。在数据合并的时候,如果制定了Replcaing的参数,当参数未指定,则保留最后一条数据,当参数已指定,则保留ver值最大的版本。数据的去重只会在数据的合并期间进行,所以常常今天数据重复了,明天看数据还没有被去重。

  • AggregateMergeTree:改变了数据片段的合并逻辑。clickhouse会将一个数据片段内所有具有相同主键的行替换为一行,这一行会存储一系列聚合函数的状态。可以使用AggregatingMergeTree表来做增量数据的聚合统计,包括物化视图的数据聚合。

  • CollapsingMergeTree:会异步的删除掉所有字段的值都相等(除了列Sign,该列一个为1,一个为-1)的成对的行,下面这两行就会被删除。

    • UserId PageViews Duration Sign
      123 5 146 1
      123 5 146 -1
    • 1是状态行,-1是状态取消行

  • VersionedCollapsingMergeTree:用于相同目的的折叠树但使用了不同的折叠算法,允许以多个线程的任何顺序插入数据。Version列有助于正确折叠行,即使他们以错误的顺序插入。而CollapsingMergeTree只允许严格的连续插入。

MergeTree #
  • 数据可以以数据片段的形式一个接一个的快速写入,数据片段在后台按照一定的规则进行合并。

  • 存储的数据按主键排序;支持数据分区(如果指定分区键的话);支持数据副本;支持数据采样。

  • 建表( 更多介绍看这里

    • CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
      (
      	name [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],
          name [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2]
          ...
          INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,
          INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2
      ) ENGINE = MergeTree()
      ORDER BY expr
      [PARTITION BY expr]
      [PRIMARY KEY expr]
      [SAMPLE BY expr]
      [TTL expr [DELETE|TO DISK 'xxx'|to volume 'xxx'],...]
      [SETTINGS name=value,..]
      
    • ENGINE:引擎名和参数

    • ORDER BY:排序键,可以是一组列的元组或任意的表达式。如果没有用PRIMARY KEY显式指定主键,clickhouse会使用排序键作为主键,如果不需要排序,可以使用ORDER BY tuple()

    • PARTITION BY:分区键,要按月分区,可以使用表达式toYYYYMM(date_column),这里date_column是一个Date类型的列,分区的格式会是YYYYMM

    • PRIMARY KEY:主键,如果要选择与排序键不同的主键,可选。大部分时间不需要再专门指定一个primary key

    • SIMPLE BY:用于抽样表达式,如果要用抽样表达式,主键中必须包含这个表达式。如SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID))

    • TTL:可以为表设置,也可以为每个列单独设置

      • 表TTL:表可以设置一个用于移除过期行的表达式,当表中的行过期时,clickhouse会删除所有对应的行。clickhouse在数据片段合并的时候会删除掉过期的数据,如果在合并的过程中执行SELECT查询,则可能得到过期的数据。
    • SETTINGS:控制MergeTree行为的额外参数

      • index_granularity:索引力度
  • 数据存储

    • 表由按主键排序的数据片段组成。
    • 当数据被插入到表中时,会创建多个数据片段并按主键的字典排序。
    • 不同分区的数据会被分成不同的片段,clickhouse在后台合并数据片段以便更高效存储。合并机制不保证具有相同主键的行全部合并到同一个数据片中。
    • 数据片段以wide或compact格式存储。wide格式下,每一列都会在文件系统中存储为单独的文件,compact模式下所有列都存储在同一个文件下。
    • 每个数据片段被逻辑分割成颗粒。颗粒是进行数据查询时最小的不可分割数据集。每个颗粒都包含整数个行。
  • 可以使用ORDER BY tuple()创建没有主键的表,这种情况下,clickhouse会根据数据插入的顺序存储。

  • 对于select查询,如果where子句中有下面这些表达式,就可以使用索引

    • 包含一个与主键/分区键中的部分字段或全部字段相等或不等的表达式。
    • 主键/分区键 in (xxx,xxx,xxx) 或者 主键/分区键 like ‘xxx’。
    • 基于主键/分区键的字段上的某些函数。
    • 基于主键/分区键的表达式的逻辑表达式。
  • TTL

    • TTL表达式的计算结果必须是日期或日期时间类型的字段

MergeTree这一块越看越迷惑,之后再来接着看吧。

日志引擎 #

  • 是为了需要写入许多小数据量(少于一百万行)的表的场景而开发的。
该引擎的共同属性 #
  1. 写入时将数据追加到文件末尾。
  2. 不支持索引。
  3. 非原子地写入数据。
  • Log和StripeLog支持并行读取数据,Log引擎将表中的每一列使用不同的文件;StripeLog将所有的数据存储到一个文件中