#19 MySQL 数据类型总结

2019-08-25

数值

  • bit [1 - 64]

  • tinyint 1

  • smallint 2
  • mediumint 3
  • int 4
  • bigint 8

  • float 4 浮点(单精度)

  • double 8 浮点(双精度)
  • decimal(m, d) max(m, d) + 2 定点

别名 Synonyms:

  • integer -> int
  • bool / boolean -> tinyint(1)
  • dec / numeric / fixed -> decimal
  • double precision / real -> double

PS: 如果开启 REAL_AS_FLOAT 模式,REAL 就变成了 FLOAT 的别名。

字符串/文本

  • char
  • varchar

  • tinytext

  • text
  • mediumtext
  • longtext

  • JSON MySQL 5.7 加入

二进制

  • binary
  • varbinary

  • tinyblob

  • blob
  • mediumblob
  • longblob

时间

类型 长度 备注
year 1 四位数:1901 ~ 2155
两位数:00 ~ 69 (2000 ~ 2069) 70 ~ 99 (1970 ~ 1999)
date 3 1000-01-01 ~ 9999-12-31
time 3 -838:59:59.999999 ~ 838:59:59.999999
即:-34d23h ~ 34d23h
datetime 8 1000-01-01 00:00:00.000000 ~
9999-12-31 23:59:59.999999
timestamp 4 1970-01-01 00:00:00 ~
2038-01-19 11:14:07
  1. 用字符串(varchar)表示时间,除了失去数据库这一层的数据类型保证之外,还会在时间比较运算场景下,失去索引命中可能。
  2. 用整型数 bigint(8 字节)表示时间,可以扩大 timestamp 时间范围,但要自己维护数据(其实还是比较好维护的)。
  3. 带时区的时间:timestamp
  4. 带精度的时间:timedatetimetimestamp,精度最大为 6,表示毫秒。比如:timestamp(6)

其他

  • enum
  • set
  • Geometry 系列

货币

  1. 以分为单位 Integer
  2. Decimal,不要用 floatdouble

参考资料与拓展阅读

#17 MySQL: wait for table metadata lock

2019-03-29

问题

mysql> status
--------------
mysql  Ver 14.14 Distrib 5.7.25, for Linux (x86_64) using  EditLine wrapper

Connection id:      17190053
Current database:   gkbb
Current user:       root@10.9.165.246
SSL:            Not in use
Current pager:      less
Using outfile:      ''
Using delimiter:    ;
Server version:     5.5.5-10.1.26-MariaDB MariaDB Server
Protocol version:   10
Connection:     10.9.108.125 via TCP/IP
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    utf8
Conn.  characterset:    utf8
TCP port:       3306
Uptime:         13 days 21 hours 14 min 38 sec

Threads: 276  Questions: 31378648  Slow queries: 212  Opens: 2977  Flush tables: 1  Open tables: 2761  Queries per second avg: 26.155
--------------

mysql> show global variables like "innodb_version";
+----------------+-------------+
| Variable_name  | Value       |
+----------------+-------------+
| innodb_version | 5.6.36-82.1 |
+----------------+-------------+
1 row in set (0.06 sec)

PS: 查看 status 还有一个快捷方式 \s

编辑测试库表结构(添加字段),卡住,任何操作都不行了,等一个多小时,还是不行。。
还一度怀疑是不是表结构设计问题,字段、数据是不是太多了。

过程

偶尔想起看看会话情况:

SELECT * FROM information_schema.processlist WHERE db = 'mydb';

或命令:

  • mysqladmin -uroot -p123456 processlist
  • mysql -uroot -p123456 -e 'SHOW PROCESSLIST'

看到里面好几个会话的状态都是 wait for table metadata lock,这就有点奇怪了,之前没有见过。

网上的资料显示:

为了在并发环境下维护表元数据的数据一致性,在表上有活动事务(显式或隐式)的时候,不可以对元数据进行写入操作。因此 MySQL 引入了 metadata lock ,来保护表的元数据信息。
因此在对表进行上述操作时,如果表上有活动事务(未提交或回滚),请求写入的会话会等待在 Metadata lock wait 。

如果资料没错,那么就是说,如果有事务没有结束,DDL 操作请求 MDL(metadata lock)时会卡住这张表。

我想起我们的服务中确实存在会话没有关闭的情况。

  1. 用了 SQLAlchemy 做 ORM
  2. 每次查询都使用一个会话,包括 SELECT
  3. 增删改操作都立即 commit 了,SELECT 却没有(记得是有个什么原因特意如此)

合理怀疑:这个查询 SESSION 没有关闭,导致 ALTER 语句进入 MDL 等待状态,然后导致了表无法进行任何操作(包括查询,至于为什么这样,我不知道)。

本地复现

  1. 开两个终端,分别建立 MySQL 连接。
  2. 其中一个终端(A):
  3. SET SESSION auto_commit = 0;
  4. SELECT * FROM test.test LIMIT 1;
  5. 另一个终端(B)只需要:TRUNCATE test.test;,然后发现:卡住了。

PS:

  1. DDL 需要 metadata 锁。
  2. TRUNCATE 属于 DDL,可能因为其非事务性(不支持提交和回滚)。参考:https://dba.stackexchange.com/questions/36607/why-is-truncate-ddl

现在,回到终端 A:

mysql> select * from information_schema.processlist where db = 'test';
+----+------+-----------+------+---------+------+---------------------------------+----------------------------------------------------------------+
| ID | USER | HOST      | DB   | COMMAND | TIME | STATE                           | INFO                                                           |
+----+------+-----------+------+---------+------+---------------------------------+----------------------------------------------------------------+
|  3 | root | localhost | test | Query   |    0 | executing                       | select * from information_schema.processlist where db = 'test' |
|  5 | root | localhost | test | Query   | 6111 | Waiting for table metadata lock | truncate test                                                  |
+----+------+-----------+------+---------+------+---------------------------------+----------------------------------------------------------------+
2 rows in set (0.00 sec)

mysql> select * from information_schema.innodb_trx\G
*************************** 1. row ***************************
                    trx_id: 421232684444408
                 trx_state: RUNNING
               trx_started: 2019-03-29 16:06:14
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 0
       trx_mysql_thread_id: 3
                 trx_query: select * from information_schema.innodb_trx
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 0
          trx_lock_structs: 0
     trx_lock_memory_bytes: 1136
           trx_rows_locked: 0
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 0
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
1 row in set (0.00 sec)

mysql> show engine innodb status\G
*************************** 1. row ***************************
  Type: InnoDB
  Name:
Status:
=====================================
2019-03-29 19:04:40 0x7f1bcc1d6700 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 3 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 7 srv_active, 0 srv_shutdown, 11228 srv_idle
srv_master_thread log flush and writes: 11234
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 16
OS WAIT ARRAY INFO: signal count 10
RW-shared spins 0, rounds 27, OS waits 12
RW-excl spins 0, rounds 32, OS waits 0
RW-sx spins 0, rounds 0, OS waits 0
Spin rounds per wait: 27.00 RW-shared, 32.00 RW-excl, 0.00 RW-sx
------------
TRANSACTIONS
------------
Trx id counter 54542
Purge done for trx's n:o < 54542 undo n:o < 0 state: running but idle
History list length 53
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421232684445328, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421232684443488, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
--------
FILE I/O
--------
I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
I/O thread 1 state: waiting for completed aio requests (log thread)
I/O thread 2 state: waiting for completed aio requests (read thread)
I/O thread 3 state: waiting for completed aio requests (read thread)
I/O thread 4 state: waiting for completed aio requests (read thread)
I/O thread 5 state: waiting for completed aio requests (read thread)
I/O thread 6 state: waiting for completed aio requests (write thread)
I/O thread 7 state: waiting for completed aio requests (write thread)
I/O thread 8 state: waiting for completed aio requests (write thread)
I/O thread 9 state: waiting for completed aio requests (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
 ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
639 OS file reads, 99 OS file writes, 21 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 4 merges
merged operations:
 insert 0, delete mark 0, delete 0
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 26417867
Log flushed up to   26417867
Pages flushed up to 26417867
Last checkpoint at  26417858
0 pending log flushes, 0 pending chkp writes
17 log i/o's done, 0.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137428992
Dictionary memory allocated 133441
Buffer pool size   8192
Free buffers       7710
Database pages     482
Old database pages 0
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 446, created 41, written 72
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 482, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
1 read views open inside InnoDB
Process ID=4492, Main thread ID=139757271107328, state: sleeping
Number of rows inserted 6, updated 0, deleted 0, read 20
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

1 row in set (0.00 sec)

表明:事务存在,TRUNCATE 锁等待。

如果,kill 3 干掉这个没有 commit 的查询 SESSION,TRUNCATE 就会正常执行下去。

结论

  1. 不管怎样,应该保持事务的短小精干,快速执行和退出。
    PS:业务代码中 SELECT 之前为什么不提交的问题需要进一步检查。
  2. DDL 需要 MDL,而没有结束的事务会阻止 MDL。
    更多细节,需要更多深入了解。

#16 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")));"

#15 Mongo 基础

2018-09-05

版本

以下是几个大版本和发布时间,作为一个大概的时间线吧:

2009/12 1.2
2010/03 1.4
2010/08 1.6
2011/03 1.8
2011/09 2.0
2012/08 2.2
2013/03 2.4
2014/04 2.6
2015/03 3.0
2015/12 3.2
2016/11 3.4
2017/11 3.6
2018/06 4.0

当前最新版本 8 月发布的 4.0.2

Update @ 2021/06/07:
之后主版本号就一直停在了 4,2020 年之后甚至一直停在了 4.4(2019 年 4.2,2020 年 4.4),这也意味着功能组件稳定下来了。

概念

在 Mongo 中,有一些名词变了,看文档的时候需要注意。

  1. 表 Table -> Collection 集合
  2. 行 Row -> Document 文档
  3. 列 Column -> Field 字段(其实 RDB 中的 Column 也经常说成字段)

数据库 Database,索引 Index 不变。

Collection

Document

  1. BSON
  2. 字段名是字符串类型(UTF-8)
  3. 字段有序
  4. 值的类型:
  5. String UTF-8 类型
  6. Int32
  7. Long
  8. Double
  9. Boolean
  10. Object
  11. Array
  12. Date
  13. ObjectId
  14. Null
  15. Date
  16. Timestamp
  17. Decimal128
  18. MinKey
  19. MaxKey
  20. Binary data
  21. JavaScript

废弃:

  1. Undefined
  2. DBPointer
  3. Symbol
  4. JavaScript code with scope

ObjectID

所有 Document 必须有一个 _id 键,没有类型限制,默认是 ObjectID 类型。
PS:所有 Document(BSON)中还包含一个时间戳,4B 时间戳 + 4B 自增数

  • 4B Unix 时间戳
  • 3B 机器标识符
  • 2B 进程号
  • 3B 随机数

关于 NoSQL

在大数据时代,传统 RDB 十分严谨的同时,效率也十分低下。
有人创造了 NoSQL 这个词,表示 Non-SQL,即不支持 SQL 的数据库。
后来部分产品添加了 SQL 支持,含义又演化成 Not Only SQL。
再后来又有人提出 NewSQL 概念,还是差不多的意思。

实现方式:

  1. 键值存储 Key-value store
  2. 文档存储 Document store
  3. 图数据库 Graph
  4. 对象数据库 Object database
  5. Tabular
  6. Tuple store
  7. Triple/quad store (RDF) database
  8. 主机式服务 Hosted
  9. 多数据库 Multivalue Database
  10. Multimodel Database

MongoDB 解决了什么问题

什么事情是 MongoDB 可以做,而 MySQL / PostgreSQL 不能做的?

特点

  • C++ 开发
  • 文档存储(JSON)
  • No Schema
  • 可以为任意属性建立索引
  • 灵活的查询语法
  • 内置小文件存储(GridFS)
  • 服务器端脚本
  • 分布式
  • 基于 AGPLv3 发布

PS:由于和云服务提供商的斗争,4.1.4 之后的所有版本(还有老版本的 BUG 修复版本)全部改用 SSPL (Server Side Public License,服务器端公共许可证) 。
该协议要求云服务提供商必须使用相同协议开放代码(之前所有主要的开源协议都没有对云服务提供商做任何限制),但 OSI 拒绝承认这是开源协议,部分 GNU/Linux 发行版将其从软件源移除。
再后来,其他开源项目,比如 Redis,Elastic,Graylog,也相继采用了和 MongoDB 相同的策略,改用了新的协议分发代码以对抗云服务提供商。

安装

sudo apt install -y mongodb
sudo systemctl disable mongodb
sudo systemctl restart mongodb
sudo systemctl status mongodb

如果希望直接从官方安装最新版本(仅支持 Ubuntu LTS 版本):

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4
echo "deb [arch=amd64] https://repo.mongodb.org/apt/ubuntu/dists/$(lsb_release -cs)/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb44.list
sudo apt update
sudo apt install -y mongodb-org
# sudo apt-mark hold mongodb-org # 避免随系统更新

配置

PS:较老的版本有一个自带的简易 Web UI,通过 httpinterface, 或者 rest 参数可以启动,3.2 之后去掉了。

配置文件

# 自带配置
dbpath=/var/lib/mongodb
logpath=/var/log/mongodb/mongodb.log
logappend=true
bind_ip = 127.0.0.1  # 修改为 0.0.0.0
# 端口是默认的 27017
journal=true

# 添加配置
replSet = markjour
# daemon 模式运行,如果用 systemd 就不需要这一行:
fork = true
sudo systemctl restart mongodb

sudo ps -ef | grep mongod
sudo netstat -antp | grep mongod

副本集

// 主节点初始化
rs.initiate();

// 查看副本集配置
cfg = rs.conf();

// 修改主节点 host 为 IP 形式
cfg.members[0].host = "<IP>:<Port>";
rs.reconfig(cfg);

// 添加从节点
rs.add("192.168.64.234");
// 如果是连接不上的节点:"Quorum check failed because not enough voting nodes responded; required 2 but only the following 1 voting nodes responded: 172.16.0.49:27017; the following nodes did not respond affirmatively: 192.168.64.234:27017 failed with Couldn't get a connection within the time limit"
rs.remove("192.168.64.234"); // 移除节点

使用

客户端 SHELL 是基于 JavaScript 的。

mongo
  • show dbs
  • db
  • use <dbName>
  • db.createCollection("s1", {capped:true, size:100000})

保留数据库名称:admin, config, local

连接字符串

https://www.mongodb.com/docs/manual/reference/connection-string/

mongodb://
[username:password@]
host1[:port1]
[,host2[:port2],...[,hostN[:portN]]]
[
    /[database]
    [?options]
]
  • 复制集(ReplicaSet)
  • replicaSet
  • 连接
  • ssl
  • connectTimeoutMS
  • socketTimeoutMS
  • 连接池
  • maxPoolSize
  • minPoolSize
  • maxIdleTimeMS
  • waitQueueMultiple
  • waitQueueTimeoutMS
  • Write Concern
  • w
  • wtimeoutMS
  • journal
  • Read Concern
  • readConcernLevel
  • Read Preference
  • readPreference
  • readPreferenceTags
  • 认证
  • authSource
  • authMedhanism
  • gssapiServiceName
  • 服务器选择与发现
  • localThresholdMS
  • serverSelectionTimeoutMS
  • serverSelectionTryOnce
  • heartbeatFrequencyMS
  • 其他
  • uuidRepresentation

参考资料与拓展阅读

#13 MySQL Boolean

2017-09-01

我们通常使用以下几种方式表示布尔值:

  1. tinyint(1)
  2. bit(1)
  3. enum('Y','N')

在 MySQL 中 bool, booleantinyint(1) 的别名。

如果只是一个 True OR False 的布尔型,没有比 bit(1) 更合适的了。
但是也有些时候,我们有好几个 bool 型用一个字段表示,最好用 bit(m),我也用过 int 型。

附:不同值的长度

Type Bit Byte Note
tinyint 8 1  
smallint 16 2  
middleint 24 3  
int 32 4  
bigint 64 8  
bit(m) m (m+7)/8  
binary[(m)]   m m 默认值:1
varbinary(m)   (m+1)  
tinyblob   (L+1) 1B 长度,最长 255B
blob[(m)]   (L+2) 2B 长度,最长 64KB - 1
mediumblob   (L+3) 3B 长度,最长 16MB - 1
longblob   (L+4) 4B 长度,最长 4GB - 1
enum('a',..)   1/2 最多可以存储 2^16 - 1 个值
set('a',..)   1/2/3/4/8 最多可以存储 64 个值

PS: blob 如果指定长度 m,MySQL 会选择足够存储数据的最小长度。
PS: MySQL set 类型

参考资料与拓展阅读

#12 数据库:应该怎么存储 IP 地址

2017-08-25

IP 地址的存储方式就两种:字符串,整型数。

表示方法

ipv4 VARCHAR(15) -- xx.xx.xx.xx
ipv6 VARCHAR(39) -- xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx

ipv4 INT UNSIGNED
ipv6 BINARY(16)

-- 如果同时需要表达 IPv4 和 IPv6
ip VARCHAR(39)
ip BINARY(16)

PS:我还在有些地方看到过这种采用 12 位整数表示 IPv4 的方法:'192.168.0.1' => 192168000001, 如果存到 MySQL 的话,只能采用 BIGINTVARCHAR(12) 了。
如果省略中间的分号,ipv6 只需要 VARCHAR(32) 就行了。

整型和字符串的转换

  • INET_ATON / INET_NTOA
  • INET6_ATON / INET6_NTOA

#11 MongoDB 聚集(数据表)中的 ID

2016-06-01

默认是一个 ObjectId 对象,也可以手动设置。

举个栗子

使用 PyMongo:

# -*- coding: utf-8 -*-

from pymongo import MongoClient

client = MongoClient()  # 连接到默认主机的默认端口:localhost:27017
db = client.test_db
collection = db.test_collection
collection.insert({"Hu" : "Ang", "Love" : [5, 'Sun', 'Xiu']})
collection.insert({"And" : 20, "Daughter" : True})
collection.insert({"GIRL": ',', "IS": "A GIRL", '_id': 123})

如果是 MongoDB 数据库操作,就应该是这样:

$ mongo
> use test_db
> db.test_collection.insert({"Hu" : "Ang", "Love" : [5, 'Sun', 'Xiu']})
> db.test_collection.insert({"And" : 20, "Daughter" : True})
> db.test_collection.insert({"GIRL": ',', "IS": "A GIRL", '_id': 123})

最后查到的结果显示如下:

> db.test_collection.find()
{ "_id" : ObjectId("5746c0f900e0990cfc600938"), "Love" : [ 5, "Sun", "Xiu" ], "Hu" : "Ang" }
{ "_id" : ObjectId("5746c0f900e0990cfc600939"), "And" : 20, "Daughter" : true }
{ "_id" : 123, "GIRL" : ",", "IS" : "A GIRL" }

_id

如果自己往里面传 _id 的话,要注意唯一性约束,如果里面存在这个 _id 值,那么就会报错:E11000 duplicate key error index

为什么没有采用像其他数据库一样的主键自增机制?

可能是因为 MongoDB 天生的分布式属性,导致其不愿耗费精力来处理自增主键的同步问题。

ObjectId

关于 ObjectId 字段,官方文档中对每个字节所表示内容的说明:

ObjectId is a 12-byte BSON type, constructed using:

  • a 4-byte value representing the seconds since the Unix epoch,
  • a 3-byte machine identifier,
  • a 2-byte process id, and
  • a 3-byte counter, starting with a random value.

ObjectId 占 12 个字节,其中:

  • 第 1、2、3、4 个字节用来存 Unix 时间戳
  • 第 5、6、7 个字节用来存机器标识
  • 第 8、9 个字节用来存客户端进程编号
    时间戳 + 机器标识 + 客户端进程编号 保证 “机器 + 进程 + 时间” 的一致性。
  • 第 10、11、12 个字节用来存随机字符串
    保证同一台机器,同一个客户端进程,在一秒种之内创建的记录的一致性。
    2 *_ (8 _ 3) = 16777216,也就是说,理论上,同一台机器,同一个客户端进程,在一秒种之内可以创建 1677 万多条记录。

举个例子,比如在 ObjectId("5746c0f900e0990cfc600939")5746c0f9 就是时间戳,00e099 就是机器标识,0cfc 就是客户端进程编号,600939 就是随机字符串。

通过这个设计,保证不同机器的 mongod 服务、同一个机器上的不同 mongod 服务进程之间都不出现重复值的情况(可能性极低,如果出现,可能也有后续的处理办法)。

重点:ObjectId 在客户端生成!!!

我个人也觉得 ObjectId 在客户端生成比服务器端要好:

  1. 更加容易根据机器标识 + 进程编号保证记录的唯一性
  2. 将生成 ObjectId 的这一部分计算转移出去,也能略微减轻 MongoDB 服务的计算压力。
  3. 客户端插入记录的时候,自己就知道 ID,不需要服务器端的反馈,针对这个设计可以设计出一些不需要返回的 insert 方法,给服务器减少一些查询带来的压力。

PyMongo 中就是使用 bson.objectid.ObjectId 生成的。可以阅读一下相关代码,了解这个 ID 的生成方法。
PS:比如,在我的 Ubuntu 环境中,代码文件就是 /usr/local/lib/python2.7/dist-packages/bson/objectid.py

参考

#10 SQL 的历史

2016-03-14

历史

  1. 70 年代初,IBM 公司开发了 SEQUEL 语言 (Structured English Query Language,结构化英语查询语言),用于管理 RDB。
  2. 70 年代末,IBM 和甲骨文分别开始开发基于 SQL 的 RDBMS。
    PS: IBM 的产品就包括大名鼎鼎的 DB2,世界上最早的 SQL 数据库。
    PS: 甲骨文当时还叫做 Relational Software, Inc
  3. 1980 年,由于商标问题,SEQUEL 改名 SQL。
    虽然官方发音是 ess-cue-el, 但至今为止,不少人还是将其读做 /ˈsiːkwəl/
  4. 1986 年被美国国家标准学会标准化(ANSI X3.135-1986)
  5. 1987 年,ISO 采纳 ANSI SQL (ISO 9075:1987),所以这个版本也被称之为 SQL87。
  6. 后来,SQL 陆续推出 89,92,1999, 2003 .... 多个版本。
    应该是 ISO 负责制定和维护吧,也无所谓啦。

版本

SQL-86 (or SQL-87) is the ISO 9075:1987 standard of 1987
SQL-89 is the ISO/IEC 9075:1989 standard of 1989
SQL-92 is the ISO/IEC 9075:1992 standard of 1992
SQL:1999 is the ISO/IEC 9075:1999 standard of 1999
SQL:2003 is the ISO/IEC 9075:2003 standard of 2003
SQL:2006 is the ISO/IEC 9075:2006 standard of 2006
SQL:2008 is the ISO/IEC 9075:2008 standard of 2008
SQL:2011 is the ISO/IEC 9075:2011 standard of 2011
SQL:2016 is the ISO/IEC 9075:2016 standard of 2016

Year Name Alias Comments
1986 SQL-86 SQL-87 First formalized by ANSI
1989 SQL-89 Minor revision that added integrity constraints
1992 SQL-92 SQL2 Major revision (ISO 9075)
1999 SQL:1999 SQL3
2003 SQL:2003
2006 SQL:2006
2008 SQL:2008
2011 SQL:2011
2016 SQL:2016
2019 SQL:2019

SQL:1999
Added regular expression matching, recursive queries (e.g. transitive closure), triggers, support for procedural and control-of-flow statements, nonscalar types (arrays), and some object-oriented features (e.g. structured types), support for embedding SQL in Java (SQL/OLB) and vice versa (SQL/JRT)

2003

Introduced XML-related features (SQL/XML), window functions, standardized sequences, and columns with autogenerated values (including identity columns)

2006
ISO/IEC 9075-14:2006 defines ways that SQL can be used with XML. It defines ways of importing and storing XML data in an SQL database, manipulating it within the database, and publishing both XML and conventional SQL-data in XML form. In addition, it lets applications integrate queries into their SQL code with XQuery, the XML Query Language published by the World Wide Web Consortium (W3C), to concurrently access ordinary SQL-data and XML documents.

2008
Legalizes ORDER BY outside cursor definitions. Adds INSTEAD OF triggers, TRUNCATE statement,[34] FETCH clause

2011
Adds temporal data (PERIOD FOR)[35] (more information at: Temporal database#History). Enhancements for window functions and FETCH clause.

2016
Adds row pattern matching, polymorphic table functions, JSON

2019
Adds Part 15, multidimensional arrays (MDarray type and operators)

https://en.wikibooks.org/wiki/Structured_Query_Language
https://en.wikibooks.org/wiki/MySQL
https://en.wikibooks.org/wiki/PostgreSQL
https://en.wikibooks.org/wiki/SQLite
https://en.wikipedia.org/wiki/SQL_reserved_words

ISO 9075

最新的 SQL 标准一共分成 9 个部分(Part 5,6,7,8,12 可能是被废弃了):

  1. Part 1: Framework (SQL/Framework)
    基本概念
  2. Part 2: Foundation (SQL/Foundation)
    基础语法
  3. Part 3: Call-Level Interface (SQL/CLI)
    应该是编程语言方面的接口
  4. Part 4: Persistent stored modules (SQL/PSM)
    SQL 面向过程编程
  5. Part 9: Management of External Data (SQL/MED)
  6. Part 10: Object language bindings (SQL/OLB)
    Java SQLJ 相关内容
  7. Part 11: Information and definition schemas (SQL/Schemata)
  8. Part 13: SQL Routines and types using the Java TM programming language (SQL/JRT)
    又是 Java 相关
  9. Part 14: XML-Related Specifications (SQL/XML)
    XML 相关

PS: 前缀 ISO/IEC 9075-<n>:2016 – Information technology – Database languages – SQL – 省略。

PS: 还有一个拓展标准:ISO/IEC 13249 SQL Multimedia and Application Packages

变种

多数数据库没有严格按照标准来实现,导致不通平台上的 SQL 语句是不能跨平台的。

以下是两种主要的 SQL 方言:

  • T-SQL(Transact-SQL): SQLServer
  • PL/SQL: Oracle