#293 MySQL 分区

2019-02-01
partition_options:
    PARTITION BY
        { [LINEAR] HASH(expr)
        | [LINEAR] KEY [ALGORITHM={1 | 2}] (column_list)
        | RANGE{(expr) | COLUMNS(column_list)}
        | LIST{(expr) | COLUMNS(column_list)} }
    [PARTITIONS num]
    [SUBPARTITION BY
        { [LINEAR] HASH(expr)
        | [LINEAR] KEY [ALGORITHM={1 | 2}] (column_list) }
      [SUBPARTITIONS num]
    ]
    [(partition_definition [, partition_definition] ...)]

partition_definition:
    PARTITION partition_name
        [VALUES
            {LESS THAN {(expr | value_list) | MAXVALUE}
            |
            IN (value_list)}]
        [[STORAGE] ENGINE [=] engine_name]
        [COMMENT [=] 'string' ]
        [DATA DIRECTORY [=] 'data_dir']
        [INDEX DIRECTORY [=] 'index_dir']
        [MAX_ROWS [=] max_number_of_rows]
        [MIN_ROWS [=] min_number_of_rows]
        [TABLESPACE [=] tablespace_name]
        [(subpartition_definition [, subpartition_definition] ...)]

subpartition_definition:
    SUBPARTITION logical_name
        [[STORAGE] ENGINE [=] engine_name]
        [COMMENT [=] 'string' ]
        [DATA DIRECTORY [=] 'data_dir']
        [INDEX DIRECTORY [=] 'index_dir']
        [MAX_ROWS [=] max_number_of_rows]
        [MIN_ROWS [=] min_number_of_rows]
        [TABLESPACE [=] tablespace_name]

分区类型

  • [LINEAR] HASH(expr) 根据值的哈希分区
  • [LINEAR] KEY [ALGORITHM={1 | 2}] (column_list)
  • RANGE{(expr) | COLUMNS(column_list)} 根据值得范围分区
  • LIST{(expr) | COLUMNS(column_list)} 根据不同的值分区

COLUMNS 不限于整数

创建分区

PARTITION BY LIST(column) (
    PARTITION a VALUES IN (a1, a2, a3),
    PARTITION b VALUES IN (b1, b2, b3),
    PARTITION c VALUES IN (c1, c2, c3)
)

PARTITION BY RANGE(column) (
    PARTITION 2012q1 VALUES LESS THAN('2012-04-01'),
    PARTITION 2012q2 VALUES LESS THAN('2012-07-01'),
    PARTITION 2012q3 VALUES LESS THAN('2012-10-01'),
    PARTITION 2012q4 VALUES LESS THAN('2013-01-01')
)

PARTITION BY HASH(column) PARTITIONS 128
PARTITION BY HASH(dayofmonth(date)) PARTITIONS 31

查看分区信息

SELECT * FROM `information_schema`.`PARTITIONS`;

子分区

  1. PARTITION 关键字换成 SUBPARTITIONPARTITIONS 关键字换成 SUBPARTITIONS,接在分区语句后面。
  2. 可以是不同类型。

比如:

PARTITION BY HASH (prod_id) SUBPARTITION BY HASH (cust_id)
PARTITIONS 4 SUBPARTITIONS 4;

脚本

如果是 By Range 分区,一般需要自动创建新的分区,删除久的分区。

比如:

CREATE TABLE `test` (
    `id` INT NOT NULL AUTO_INCREMENT,
    `date` DATE NOT NULL,
    `key` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci',
    `value` VARCHAR(300) NOT NULL COLLATE 'utf8mb4_general_ci',
    `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`, `date`) USING BTREE,
    UNIQUE INDEX `key` (`date`, `key`) USING BTREE
)
COLLATE='utf8mb4_general_ci'
/*!50100 PARTITION BY RANGE (to_days(`date`))
(PARTITION p20230123 VALUES LESS THAN (738909) ENGINE = InnoDB,
 PARTITION p20230124 VALUES LESS THAN (738910) ENGINE = InnoDB,
 PARTITION p20230125 VALUES LESS THAN (738911) ENGINE = InnoDB)  */;

然后,通过下面这个 cron 任务自动更新分区:

#!/bin/bash

# 开启调试模式,输出每条执行的命令及其执行结果
set -x

# 检查当前机器 IP 地址中是否包含指定的 VIP(虚拟 IP)
# 确认在主 MySQL 上执行
vip_w="192.168.12.34"
if [ $(/sbin/ip a | grep "${vip_w}" | wc -l) -eq 0 ]; then echo 'WARN: Wrong Machine!!!'; exit 1; fi

# 删除 90 天前的分区
# PS:如果分区不存在,TRUNCATE 不会报错。
delete_date=$(date -d '90 days ago' +%Y%m%d)
mysql -uroot -p123456 -e "USE test; ALTER TABLE test TRUNCATE PARTITION p$delete_date;"  # DROP

# 创建未来分区
create_date=$(date -d '7 days' +%Y%m%d)
mysql -uroot -p123456 -e "USE test; ALTER TABLE test ADD PARTITION (PARTITION p$delete_date VALUES LESS THAN (TO_DAYS("$delete_date")));"

#292 PuTTY 配色

2019-01-30

参考:https://github.com/altercation/solarized/blob/master/putty-colors-solarized/solarized_dark.reg

Colour21\255,255,255\
Colour20\187,187,187\
Colour19\85,255,255\
Colour18\0,187,187\
Colour17\255,85,255\
Colour16\187,0,187\
Colour15\85,85,255\
Colour14\0,0,187\
Colour13\255,255,85\
Colour12\187,187,0\
Colour11\85,255,85\
Colour10\0,187,0\
Colour9\255,85,85\
Colour8\187,0,0\
Colour7\85,85,85\
Colour6\0,0,0\
Colour5\0,255,0\
Colour4\0,0,0\
Colour3\85,85,85\
Colour2\0,0,0\
Colour1\255,255,255\
Colour0\187,187,187\
Colour0\131,148,150\
Colour1\147,161,161\
Colour2\0,43,54\
Colour3\7,54,66\
Colour4\0,43,54\
Colour5\238,232,213\
Colour6\7,54,66\
Colour7\0,43,56\
Colour8\220,50,47\
Colour9\203,75,22\
Colour10\133,153,0\
Colour11\88,110,117\
Colour12\181,137,0\
Colour13\101,123,131\
Colour14\38,139,210\
Colour15\131,148,150\
Colour16\211,54,130\
Colour17\108,113,196\
Colour18\42,161,152\
Colour19\147,161,161\
Colour20\238,232,213\
Colour21\253,246,227\

#291 电子邮件的一些小技巧

2019-01-14

紧急程度 X-Priority

有另一个 Priority 头,不知道为什么没有使用。

  • 1:最高
  • 2:高级
  • 3:一般
  • 4:低级
  • 5:最低

根据网上的一些资料,这几个头已经被垃圾邮件滥用,导致可能会被拦截。
不过,企业邮箱使用应该没有问题。

网易邮箱发送的时候勾选重要,就会设置 X-Priority: 1

需要发送回执 Disposition-Notification-To

效果就是客户端会提示:“是否发送回执”,确认 or 取消。
注意,不是所有客户端会支持回执。

mdn-request-header = "Disposition-Notification-To" ":" 1#mailbox
# mailbox 就是 local-poart@domain 这种格式
# RFC 中的这个 1# 我也不知道是干嘛用的

值是正常的邮件地址格式就行了,比如:

Disposition-Notification-To: admin@example.com

Disposition-Notification-To: =?utf-8?b?566h55CG5ZGY?= admin@example.com

注意:会和 Return-Path 的域名部分做对比,如果不一致,不会发送回执。

参考:https://www.rfc-editor.org/rfc/rfc2298.html

回执参考

主要是有这么一段:

Content-Type: message/disposition-notification; name="MDNPart2.txt"
Content-Disposition: inline
Content-Transfer-Encoding: 7bit

Reporting-UA: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.7.0
Final-Recipient: rfc822;huang@example.com
Original-Message-ID: <2ec11bc0.59a.1803f61e0f9.Coremail.admin@example.com>
Disposition: manual-action/MDN-sent-manually; displayed

参考资料与拓展阅读

#290 Python3 中的 Bytes 索引取值

2019-01-13

今天研究 secure cookie (安全的 Cookie) 的时候发现这个问题,觉得很好玩,之前还没有遇到过,没有注意。

a = b'hello'
a[0]
# 104

Python3 对 Bytes 类型索引取值返回的是一个整数!!!

这个以后需要小心,如果要像我们预期的那样,取一个字符的话,应该这样:

a = b'hello'
a[0:1]
# h

#288 日程邀请邮件

2019-01-10

经过测试,至少 QQ 和 网易邮箱的 WebMail 都提供了日程的支持。
Thunderbird 只能通过日历 App 打开附件 (ics 文件) 的方式添加日程。

效果

event mail

原理

在邮件中插入了一个 text/calendar 类型的附件:

Content-Type: text/calendar; charset=utf-8; method=REQUEST;
    name=ATT1547083200618.ics
Content-Transfer-Encoding: base64
Content-Disposition: inline; filename="ATT1547083200618.ics"

解析出来这样一个 iCal 文件:

BEGIN:VCALENDAR
PRODID:-//Netease//WebMail
VERSION:2.0
METHOD:REQUEST
BEGIN:VEVENT
SUMMARY:日程:上午十点的面试
LOCATION:小会议室
DTSTART:20190110T013000Z
DTEND:20190110T023000Z
UID:11c27eb7-adab-4549-8aea-efadcec7bb6c
SEQUENCE:0
STATUS:CONFIRMED
ORGANIZER;CN=张三:mailto:zhangsan@example.com
ATTENDEE:mailto:lisi@example.com
END:VEVENT
END:VCALENDAR

回复

这个回复就各异了,没有同意的格式。
QQ 邮件只会有一个 自动回复: xxxx,不知道是接受还是拒绝。
QQ 企业邮件会有详细的信息,附带了原日程,并有一句话:xxx 已经接受你的邀请: 日程:xxx

#287 常见 Web 缓存服务

2019-01-06

Apache,Nginx 都有缓存功能,再加上 Redis 做动态数据的缓存,再加上 CDN,所以我还没有用过专门的缓存服务。
但是这些服务真是大名鼎鼎,即便不用,我也可以先列出来做个简单的了解。

#286 Git Submodule

2019-01-03

命令

usage: git submodule [--quiet] [--cached]
   or: git submodule [--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
   or: git submodule [--quiet] status [--cached] [--recursive] [--] [<path>...]
   or: git submodule [--quiet] init [--] [<path>...]
   or: git submodule [--quiet] deinit [-f|--force] (--all| [--] <path>...)
   or: git submodule [--quiet] update [--init [--filter=<filter-spec>]] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] [--] [<path>...]
   or: git submodule [--quiet] set-branch (--default|--branch <branch>) [--] <path>
   or: git submodule [--quiet] set-url [--] <path> <newurl>
   or: git submodule [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
   or: git submodule [--quiet] foreach [--recursive] <command>
   or: git submodule [--quiet] sync [--recursive] [--] [<path>...]
   or: git submodule [--quiet] absorbgitdirs [--] [<path>...]

相关文件

  1. .gitmodules

    [submodule "<moduleName>"]
        path = <moduleDir>
        url = <repoAddr>
    
  2. .git/config 中有相近的 section:

    [submodule "<moduleName>"]
        active = true
        url = <repoAddr>
    
  3. .git 目录在 .git/modules/<moduleName>

克隆

参考:https://stackoverflow.com/questions/3796927/how-to-git-clone-including-submodules

git clone --recurse-submodules -j8 github.com:shouce/shouce.git # 2.13+
git clone --recursive -j8 github.com:shouce/shouce.git # 1.6.5+

-j 表示子模块并发操作,每次 n 个子模块。

针对更老的版本或者以存在的库:

git clone github.com:shouce/shouce.git
cd shouce
git submodule update --init --recursive

发现一个 clone 参数 --[no-]shallow-submodules,可以使每个子模块仓库的克隆 deepth 为 1,应该是用得上的。

已存在的项目

可能是克隆的时候没有克隆子仓库,也可能是后面添加进来的子仓库。

$ git submodule init
Submodule '<moduleName>' (<repoAddr>) registered for path '<moduleDir>'

$ git submodule update
Cloning into '<moduleFullPath>'...
Submodule path '<moduleDir>': checked out '<commitID>'

或者二合一:

git submodule update --init --recursive

后面有更新就进入子模块 git pull。

添加子模块

git submodule add -b dev --name devtools gitee.com:catroll/devtools tools/dev
# [submodule "devtools"]
#      path = tools/dev
#      url = gitee.com:catroll/devtools
#      branch = dev

删除子模块

$ git submodule deinit <moduleDir>
Cleared directory '<moduleDir>'
Submodule '<moduleName>' (<remoteAddr>) unregistered for path '<moduleDir>'

作用:

  1. 清空子模块目录下的所有文件
  2. 去掉 .git/config 子模块配置

这个操作之后:git status 没有任何变化,.gitmodule 还保留着。

然后:

  1. 修改 .gitmodules
  2. 删除子模块目录
  3. 提交变更
  4. 推送到远程仓库

#284 Python 定时任务的简单部署

2018-12-20

部署

# curl: (35) SSL connect error
# StackOverflow: You are using a very old version of curl.
# yum upgrade curl

# 安装 pyenv
curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash

# 环境变量
PYENV_ROOT="$HOME/.pyenv"
PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

# 可用版本列表
pyenv install -l
# 使用国内 Python 镜像,相当于给 pyenv 加速
wget https://mirrors.sohu.com/python/3.6.7/Python-3.6.7.tar.xz -P ~/.pyenv/cache/
# 安装(优先使用缓存目录中的文件,会检查缓存校验码是否正确)
pyenv install 3.6.7

运行方式

假定:

  • 项目路径:/path/to/project/
  • 定时任务命令:python main.py

crontab 配置

早上 01:15 执行某某定时任务:

15 1 * * * /bin/bash /path/to/project/cron.sh

脚本 cron.sh

pyenv + pip 模式

#!/bin/bash

# 环境变量
PYENV_ROOT="$HOME/.pyenv"
PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

cd /path/to/project/

# 创建并激活虚拟环境,安装依赖
# $PYENV_ROOT/versions/3.6.7/envs/tasks/
pyenv virtualenv 3.6.7 tasks

if [ $? -ne 0 ]; then
    pyenv activate tasks
    pip install -r requirements.txt
else
    pyenv activate tasks
fi

# 运行脚本
python main.py

pyenv + pipenv 模式

#!/bin/bash

# 环境变量
PYENV_ROOT="$HOME/.pyenv"
PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

cd /path/to/project/

# 选择 Python 版本
pyenv local 3.6.7

pip show pipenv > /dev/null

if [ $? -ne 0 ]; then
    # 安装 pipenv
    pip install pipenv
    # 创建虚拟环境,安装依赖
    pipenv install
fi

# 运行脚本
pipenv run python main.py