MySQL索引优化

不使用索引的情况

  1. 如果 mysql 估计使用索引比全表扫描更慢,则不使用索引。
    例如:如果 key_part 1 均匀分布在 1 和 100 之间,下列查询中使用索引就不是很好:
1
SELECT * FROM table_name where key_part1 > 1 and key_part1 < 90;
  1. 如果使用 heap 表并且 where 条件中不用=索引列,其他 > 、 < 、 >= 、 <= 均不使 用索引(MyISAM 和 innodb 表使用索引);

  2. 使用 or 分割的条件,如果 or 前的条件中的列有索引,后面的列中没有索引,那么涉及到的索引都不会使用。

  3. 如果创建复合索引,如果条件中使用的列不是索引列的第一部分;(不是前缀索引)

  4. 如果 like 是以%开始;

  5. 对 where 后边条件为字符串的一定要加引号,字符串如果为数字 mysql 会自动转 为字符串,但是不使用索引。

查看索引使用情况

如果索引正在工作, Handler_read_key 的值将很高,这个值代表了一个行被索引值读的次数,很低的值表明增加索引得到的性能改善不高,因为索引并不经常使 用。

Handler_read_rnd_next 的值高则意味着查询运行低效,并且应该建立索引补救。这个值的含义是在数据文件中读下一行的请求数。如果你正进行大量的表扫描,

该值较高。通常说明表索引不正确或写入的查询没有利用索引。

语法:

1
show status like 'Handler_read%';
阅读更多

MySQL索引小结

索引的作用

索引的出现其实就是为了提高数据查询的效率,用于快速找出在某个列中有一特定值的行。

阅读更多

MySQL临时表

MySQL 临时表分为“内存临时表”和“磁盘临时表”,其中内存临时表使用 MySQL 的 MEMORY 存储引擎,磁盘临时表使用 MySQL 的 MyISAM 存储引擎;一般情况下,MySQL 会先创建内存临时表,但内存临时表超过配置指定的值后,MySQL 会将内存临时表导出到磁盘临时表。
MySQL 临时表在我们需要保存一些临时数据时是非常有用的。临时表只在当前连接可见,当关闭连接时,Mysql 会自动删除表并释放所有空间。

阅读更多

redis cluster的批量操作

之前项目中都在使用redis的单机或者主从,后来迁移到集群的时候,出现了一系列问题,下面整理一下redis集群方面的知识。

阅读更多

MongoDB索引笔记

在 MongoDB 查询过程中,索引(Index) 起到非常重要的作用,如果没有索引,MongoDB将会执行全表扫描 。当然如果数据量比较少全表扫描的开销并不大,但如果集合文档数量到百万、千万甚至上亿的时候,一个查询耗费数十秒甚至几分钟都有可能,代价非常高昂。

阅读更多

记一次MySQL大表空间收缩

最近收到 MySQL 数据库磁盘空间使用率过高的报警,发现其中的一个表空间占用竟然高达三百多 G。删除了一半多的数据后,可是表文件大小还是没变,当时就懵了。
请教 DBA 才知道删除大量数据后存在数据空洞,MySQL 不会自动收缩表空间,需要手工操作。

产生原因

  1. InnoDB 里的数据都是用 B+树的结构组织的,每当删除了一条记录后,InnoDB 引擎只会把这个记录标记为删除,而在一段时间内的大量删除操作,会使这种留空的空间变得比存储列表内容所使用的空间更大。

  2. 记录的复用只限定符合范围条件的数据。比如说删除一条 ID 为 400 的数据,当执行插入操作 ID 为 400 前后的数据时,MySQL 可能会复用这个位置。但如果某个空白空间一直没有被大小合适的数据占用,仍然无法将其彻底占用,就形成了碎片。

  3. 如果是随机插入数据也会造成数据空洞。

解决方案

  1. 对数据导出后进行收缩,然后导入数据。
  2. 重建表

目前没有导出数据的条件,采用重建表的方法,可以采用alter table A engine=InnoDB 命令。
MySQL 5.6 版本开始引入了 Online DDL,允许在表上执行 DDL 的操作(比如创建索引)的同时不阻塞并发的 DML 操作 和 查询(select)操作。

  • Algorithm=Inplace :为了避免表拷贝导致的实例性能问题(空间、I/O 问题),建议在 DDL 中包含该选项。如果 DDL 操作不支持 Algorithm=Inplace 方式,DDL 操作会立刻返回错误。

  • Lock=None :为了在 DDL 操作过程中不影响业务的 DML 操作,建议在 DDL 中包含该选项。如果 DDL 操作不支持 Lock=None (允许并行 DML 操作)选项,DDL 操作会立刻返回错误。

所有的 DDL 操作均建议在业务低峰期进行,避免对业务产生影响。经过测试,删除表的数据后,对应的磁盘空间也正常释放了。

MongoDB-aggregate用法

MongoDB 中聚合(aggregate)方法可以对集合中的文档进行变换和组合,主要用于处理数据。语法:

1
db.collection.aggregate(pipeline, options);

管道操作符

MongoDB 的聚合管道将 MongoDB 文档在一个管道处理完毕后将结果传递给下一个管道处理,管道操纵是可以重复的。

管道聚合阶段:

1
2
3
4
5
6
7
$project:包含、排除、重命名和显示字段
$match:查询,需要同find()一样的参数
$limit:限制结果数量
$skip:忽略结果的数量
$sort:按照给定的字段排序结果
$group:按照给定表达式组合结果
$unwind:分割嵌入数组到自己顶层文件<br>

group 查询操作符:

1
2
3
4
5
6
7
8
$sum 总结从集合中的所有文件所定义的值.
$avg 从所有文档集合中所有给定值计算的平均.
$min 获取集合中的所有文件中的相应值最小.
$max 获取集合中的所有文件中的相应值的最大.
$push 值插入到一个数组生成文档中.
$addToSet 值插入到一个数组中所得到的文档,但不会创建重复.
$first 根据分组从源文档中获取的第一个文档。通常情况下,这才有意义,连同以前的一些应用 “$sort”-stage.
$last 根据分组从源文档中获取最后的文档。通常,这才有意义,连同以前的一些应用 “$sort”-stage.

附加选项

  • explain:布尔值,指定返回结果是否显示该操作的执行计划
  • allowDiskUse:布尔值,指定该聚合操作是否使用磁盘。
    每个阶段管道限制为 100MB 的内存。如果一个节点管道超过这个极限,MongoDB 将产生一个错误。为了能够在处理大型数据集,可以设置 allowDiskUse 为 true 来在聚合管道节点把数据写入临时文件。这样就可以解决 100MB 的内存的限制。
  • cursor
  • maxTimeMS
  • bypassDocumentValidation
  • readConcern
  • collation

参考文档

https://docs.mongodb.com/manual/reference/method/db.collection.aggregate/#db.collection.aggregate
http://www.mongodb.org.cn/tutorial/19.html

Redis基础数据类型

Redis 有 5 种基础数据结构,分别为:String (字符串)、Hash (哈希)、List (列表)、Set (集合) 和 Sorted Set (有序集合)

String (字符串)

String 数据结构是简单的 key-value 类型。
最常见的用途就是缓存信息。我们可以将需要缓存结构体使用 JSON 序列化成字符串来缓存,取出来的时候再反序列化一下。

常用命令 说明 示例
set 设置指定 key 的值 set name zubin
get 获取指定 key 的值 get name
del 删除指定 key del key
mset 批量设置指定 key 的值 mset name zubin age 18
mget 批量获取指定 key 的值 mget name age
setex 如果设置指定 key 的值和过期时间(秒) setex name 5 zubin
setnx 只有 key 不存在时设置 key 的值 setnx name zubin
incr 将 key 中存储的数字值增 1 incr age
incyby 将 key 所储存的值加上给定的增量值 incrby age 5
decr 将 key 中存储的数字值减 1 decr age
strlen 返回指定 key 中 value 的长度 strlen name
append 将字符串追加到指定 key 中值的末尾。 append name 1

Hash (哈希)

Redis hash 是一个 string 类型的 field 和 value 的映射表,是无序字典, 特别适合用于存储对象。每个 hash 可以存储2^32 - 1(4294967295) 键值对。

常用命令 说明 示例
hset 设置 hash 表中 field 的值 hset user name zubin
hmset 设置 hash 表中多个 field 的值 hmset user name zubin age 18
hsetnx field 不存在时设置哈希表字段的值 hsetnx user name zubin
hget 获取指定字段的值 hget user name
hmget 获取多个字段的值 hmget user age name
hgetall 获取 hash 表中所有字段的值 hgetall user
hdel 删除一个或多个 hash 字段 hdel user age
hexists 判断 hash 表中指定的字段是否存在 hexists user age
hincrby 为 hash 表中指定的整数值字段加上增量 hincrby user age 5
hkeys 获取 hash 表中所有的字段 hkeys user
hvals 获取 hash 表中所有的值 hvals user
hlen 获取 hash 表中字段的数量 hlen user

List (列表)

Redis 列表是简单的字符串列表,按照插入顺序排序,注意它是链表而不是数组,一个列表最多可以包含 2^32 - 1 个元素 (4294967295, 每个列表超过 40 亿个元素)。
list 的插入和删除操作非常快,时间复杂度为 O(1),但是索引定位很慢,时间复杂度为 O(n)。
list 常用来做异步队列使用,你可以添加一个元素到列表的头部(左边)或者尾部(右边)。

常用命令 说明 示例
lpush 将一个或多个值插入到列表头部 lpush skills Node Golang Java
lpushx 将一个或多个值插入到已存在的列表头部 lpushx skills Node Golang Java
lpop 移除列表第一个元素 lpop skills
blpop 移除列表第一个元素, 列表为空会阻塞,直到超时或者有可移除的元素 blpop skills 100
rpush 将一个或多个值插入到列表尾部 rpush skills Node Golang Java
rpushx 将一个或多个值插入到已存在的列表尾部 rpushx skills Node Golang Java
rpop 移除列表最后一个个元素 rpop skills
brpop 移除列表最后一个个元素, 列表为空会阻塞,直到超时或者有可移除的元素 brpop skills 100
brpoplpush 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它 brpoplpush skills list1 500
lindex 通过索引获取列表中的元素 lindex skills 1
lset 通过索引来设置元素的值 lset skills 0 Python
llen 获取列表长度 llen skills
lrange 获取列表指定范围内的元素 lrange skills 1 2
lrem 根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素 lrem skills 0 node
ltrim 修剪列表,只保留指定区间内的元素 ltrim skills 1 2

Set (集合)

Redis 的 Set 是 String 类型的无序集合。它内部的键值对是无序的唯一的,这就意味着集合中不能出现重复的数据,集合中最大的成员数为 2^32 - 1 (4294967295)。
Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

常用命令 说明 示例
sadd 向集合添加一个或多个元素 sadd skills python go java go
scard 获取集合的成员数 scard skills
sismember 判断是否集合中的成员 sismember skills go
smembers 返回集合中的所有成员 smembers skills
spop 随机移除并返回集合中的一个元素 spop skills
sinter 返回指定集合的交集 sinter key1 key2
sunion 返回指定集合的并集 sunion key1 key2
sdiff 返回指定集合的差集 sdiff key2 key2

Sorted Set (有序集合)

Redis 有序集合和集合一样也是 string 类型元素的集合,一方面它是一个 set,保证了内部 value 的唯一性,另一方面它可以给每个 value 赋予一个 score(分数),代表这个 value 的排序权重。集合中最大的成员数为 2^32 - 1(4294967295)。
Redis 正是通过分数来为集合中的成员进行从小到大的排序,有序集合的成员是唯一的,但分数(score)却可以重复。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

常用命令 说明 示例
zadd 添加一个或多个成员或者更新已存在成员的分数 zadd userscore zubin 1
zcard 获取有序集合的成员数 zcard userscore
zcount 获取有序集合指定区间分数的成员数 zcount userscore 1 3
zincrby 给指定成员的分数加上增量 zincrby userscore 1 zubin
zrange 返回指定区间内的成员 zrange userscore 0 -1
zrangebyscore 返回 score 区间内的成员 zrangebyscore userscore -inf +inf
zrem 移除一个或多个元素 zrem userscore 0 -1
zrevrange 返回指定分数区间内的成员,按分数递减 zrevrange userscore 0 -1
zrevrank 返回成员 member 的排名 zrevrank userscore zubin
zscore 返回 score 的 score 值 zscore userscore zubin

数据库设计-约束

数据库约束是为了保证数据的完整性(正确性)而实现的一套机制。主要分为五大约束:

  1. 主键约束(Primay Key Coustraint): 唯一性,非空性
1
2
3
# 添加主键约束(将UserId作为主键)
alter table UserId
   add constraint PK_UserId primary key (UserId);
  1. 唯一约束 (Unique Counstraint):唯一性,可以空,但只能有一个
1
2
3
# 添加唯一约束(身份证号唯一,因为每个人的都不一样)
alter table UserInfo
   add constraint UQ_IDNumber unique(IdentityCardNumber);
  1. 检查约束 (Check Counstraint):对该列数据的范格式的限制(如:年性别等)
1
2
3
4
5
# 添加检查约束 (对年龄加以限定 20-40 岁之间)
alter table UserInfo
   add constraint CK_UserAge check (UserAge between 20 and 40);
alter table UserInfo
   add constraint CK_UserSex check (UserSex=’男’ or UserSex=’女′);
  1. 默认约束 (Default Counstraint):该数据的默认值
1
2
3
# 添加默认约束(如果地址不填 默认为“地址不详”)
alter table UserInfo
   add constraint DF_UserAddress default (‘地址不详’) for UserAddress;
  1. 外键约束 (Foreign Key Counstraint):需要建立两表间的关系并引用主表的列
1
2
3
# 添加外键约束 (主表 UserInfo 和从表 UserOrder 建立关系,关联字段 UserId)
alter table UserOrder
   add constraint FK_UserId_UserId foreign key(UserId)references UserInfo(UserId);
阅读更多