elasticsearch知识整理

ElasticSearch

elasticsearch 是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。

特点

  • 分布式,无需人工搭建集群(solr就需要人为配置,使用Zookeeper作为注册中心)
  • Restful风格,一切API都遵循Rest原则,容易上手
  • 近实时搜索,数据更新在Elasticsearch中几乎是完全同步的。

    一、安装

    1、docker方式安装elasticsearch

  • 安装
    1
    $ docker pull elasticsearch
  • 启动 (java程序访问可以在增加一个9300端口)
    1
    $ docker run --name elasticsearch -p 9200:9200  -e "discovery.type=single-node" -d elasticsearch
  • 进入容器修改配置
    1
    2
    3
    $ docker exec -it elasticsearch /bin/bash
    $ cd /usr/share/elasticsearch/config/
    $ vi elasticsearch.yml
  • 追加一下内容,解决跨域问题
    1
    2
    http.cors.enabled: true
    http.cors.allow-origin: "*"
  • 重启容器
    1
    2
    $ exit
    $ docker restart elasticsearch
    默认访问链接http://127.0.0.1:9200就可以打开kibana的界面了。

    2、安装ik分词器

    es自带的分词器对中文分词不是很友好,elasticsearch的版本和ik分词器的版本需要保持一致;
    1
    2
    3
    4
    $ cd /usr/share/elasticsearch/plugins/
    $ elasticsearch-plugin install 对应版本的安装包地址(https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.2.0/elasticsearch-analysis-ik-7.2.0.zip)
    $ exit
    $ docker restart elasticsearch 可以在kibana界面的dev tools中验证是否安装成功

    3、docker安装kibana

    可视化elasticsearch管理页面
  • 安装 (要与es版本对应)
    1
    $ docker pull kibana
  • 启动kibana(使用–link连接到elasticsearch容器 ,或者使用Network让其在同一个网络下)
    1
    2
    $ docker run --name kibana  -p 5601:5601 -d kibana
    $ docker start kibana
    浏览器输入http://127.0.0.1:5601就可以打开kibana的界面了。
  • 遇到问题
    1)访问提示 Kibana server is not ready yet
    第一:把KB和ES版本调整为统一版本
    第二:将配置文件kibana.yml中的elasticsearch.url改为正确的链接
    第三:保持kibana和elasticsearch在同一个网络模式下,可以使用默认的bridge网络下的段的地址172.17.0.x
    注意,默认为bridge网络,此网络下两个容器无法用127.0.0.x 的本地ip进行通信访问,如果想用要都切换到host模式

二、常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
GET _search
{
"query": {
"match_all": {}
}
}
GET _cluster/health
GET _cat/nodes

#创建,自动生成id
POST users/_doc
{
"user":"Mike",
"post_date":"2019-04-15T12:12:12",
"message":"trying out Kibana"
}
#创建,手动指定id,id存在报错
PUT users/_doc/1?op_type=create
{
"user":"Mike",
"post_date":"2019-04-15T12:12:12",
"message":"trying out Kibana123123"
}
#根据id查询一条
GET users/_doc/1

#(index方式创建)删除原来的文档,写入新的,版本加一
PUT users/_doc/1
{
"user":"MIke"
}
#根据ID修改文档的内容,没有的字段会新增
POST users/_update/1/
{
"doc":{
"post_date":"2019-04-15T12:12:12",
"message":"trying out Kibana123123"
}
}
#根据删除
DELETE users/_doc/1

#批量增删改
PUT _bulk
#根据id批量读
GET _mget
#读全部
POST kibana_sample_data_ecommerce/_msearch
{}
{"query":{"match_all":{}},"size":1}
#match:会将搜索田间分词,然后到索引里面找
#match_phrase:习语匹配,查询确切的phase,在对查询字段定义了分词器的情况下,会使用分词器对输入进行分词,然后返回满足下述两个条件的document:
#term 是代表完全匹配(相当mysql与全等用 )

三、索引settings和mappings

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
PUT loaravel-log-2020.11.05
{
"settings": {
"number_of_shards": 2,//主分片数
"number_of_replicas": 1 //每一个主分片有多少个副本分片
},
"mappings": {
"properties": {
"path": {
"type": "keyword"
},
"type": {
"type": "keyword"
},
"messgae": {
"type": "text"
},
"tags": {
"type": "text",//可以寸数组
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"host": {
"type": "keyword"
},
"@timestamp": { //带@的是系统预留字段 固定类型
"type": "date"
},
"@version": {
"type": "text"
}
}
}
}

四、重要知识点

查询搜索条件算分
索引的命名规则
创建一个新indx时 设置 setting mapping(字段类型,分词规则)
index template
聚合统计搜索使用(aggs) bucket按照规则诗句划分不同的组(相当于mysql group),metric 用于数据集的统计(可以统计bucket生成的数据集中的字段进行分析,例如平均值)
search -》协调节点-》查询所有分片-》取回id和排序合并-》去对应分片的数据节点取出具体doc信息
主节点 index(存储索引index信息,索引关联对应的分片) -》

分布式知识整理

一、分布式事务

1、tcc(最终一致性)

  • 一般使用tcc分布式事务框架
  • 实现的功能需要修改的代码比较多,需要个个系统配合完成try、commit和cancel方法
  • 有try、commit和cancel三个阶段
  • TCC是业务层面的分布式事务,最终一致性,不会一直持有资源的锁。
  • try阶段保证各自服务本地资源一致
  • commit 和cancel只会有一个被执行
  • try阶段有服务失败将触发cancel 如果cancel失败,那么走补偿机制,保证最终一致性
  • 不会阻塞资源
  • 执行流程
    1) 成功流程:事务框架->try(请求所有服务预留业务资源,不阻塞)->成功->commit(所有服务正式提交修改)
    2) 失败流程:事务框架->try(请求所有服务预留业务资源,不阻塞)->失败->cancel(告知所有服务取消预约)
    3) commit/cancel 如果失败了,那么走补偿机制,保证最终一致性

    3、xa两段提交(强一致性)(阻塞)

  • 分为协调者(xa协调工具)、参与者(各服务)
  • XA是资源层面的分布式事务,强一致性,在两阶段提交的整个过程中,一直会持有资源的锁。
  • 只有try 和commit 两个阶段
  • MySQL中只有InnoDB引擎支持XA协议
  • MySQL中只有当隔离级别为Serializable(最高界别,串行)时候才能使用分布式事务
  • 执行流程
    1) 成功流程:协调者->try(询问所有服务是否可以执行,持有资源的锁)->成功->协调者通知commit(所有服务正式提交修改),解除资源锁定
    2) 失败流程:协调者->try(询问所有服务是否可以执行,持有资源的锁)->失败->协调者通知rollback,解除资源锁定

2、mq事务消息(最终一致性)

  • 实现的功能需要修改的代码相对较少,消费端几乎不需要任何修改
  • 只能保证(生产端的业务和消息投递到mq同时成功)
  • 消费端消费失败走补偿机制,保证最终一致性
  • 执行流程
    1) 服务a向mq发送prepare消息(本地事务,消费端不会收到)->mq得到返回给服务a执行结果->只能服务a本地业务
    2) 服务a本地业务执行成功->向mq发送事务消息的提交commitlog->消息出现在真实的mq消息中->消费端接收处理
    3) 服务a本地业务执行失败->向mq发送事务消息的撤销commitlog->移除事务消息
    4) 如果mq未收到事务的commit/rollback则会回查事务状态,然后继续执行

    4、最大努力通知

  • 不可靠消息:业务活动主动方,在完成业务处理之后,向业务活动的被动方发送消息,直到通知N次后不再通知,允许消息丢失(不可靠消息)。
  • 定期校对:业务活动的被动方,根据定时策略,向业务活动主动方查询(主动方提供查询接口),恢复丢失的业务消息。

    5、可靠消息最终一致性

  • 是指产生消息的业务动作与消息发送的一致
  • 如果业务操作成功,那么由这个业务操作所产生的消息一定要成功投递出去
  • 异步确保性
  • 一般可以使用mq事务消息实现

    二、分布式锁

    1、基于缓存(Redis等)实现分布式锁

  • 主要使用命令:
    1)SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。
    2)expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。
    2)delete key :删除key。
  • 实现思想:
    1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。
    2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
    3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

2、基于Zookeeper实现分布式锁

因为需要频繁的创建和删除节点,性能上不如Redis方式。

  • 实现思想:
    1)创建一个目录mylock;
    2)线程A想获取锁就在mylock目录下创建临时顺序节点;
    3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;
    4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点;
    5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。

3、基于数据库实现分布式锁;

性能差,不推荐,不需要了解

redis知识点整理(持续更新)

redis

一、redis支持的数据类型

1、RedisObject数据结构

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct redisObject {
// 对外的类型 string list set hash zset等 4bit
unsigned type:4;
// 底层存储方式 4bit
unsigned encoding:4;
// LRU 时间 24bit
unsigned lru:LRU_BITS;
// 引用计数 4byte
int refcount;
// 指向对象的指针 8byte
void *ptr;
} robj;

string(字符串)

  • 描述:
    二进制安全,基本的常用的数据类型,大能存储 512MB
  • 基本用法:
    1、写入: SET key “value”
    2、读取: GET key
  • 使用场景:
    常用的kv存取
  • 数据编码方式
    当存储的字符串全是数字时,此时使用int方式来存储;
    当存储的字符串长度小于44个字符时,此时使用embstr方式来存储;
    当存储的字符串长度大于44个字符时,此时使用raw方式来存储;

    hash(哈希)

  • 描述:
    是一个键值(key=>value)对集合,也就是一个key对应多个kv
  • 基本用法:
    1、写入:
    多个kv:HMSET hash_key field_key1 “val1” field_key2 “val2”
    一个kv:HSET hash_key field_key3 “val3”
    2、读取:
    HGET hash_key field_key1
  • 使用场景:
    例如存用户信息

    list(列表)

  • 描述:
    按照插入顺序排序,你可以添加一个元素到列表的头部(左边)或者尾部(右边),常用实现队列,一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)
  • 基本用法:
    1、头部插入:
    LPUSH listKey val1
    LPUSH listKey val2
    LPUSH listKey val3
    尾部取出:
    RPOP listKey //val3

2、尾部插入:
RPUSH listKey val1
RPUSH listKey val2
RPUSH listKey val3
头部取出:
RPOP listKey //val1

  • 使用场景:
    队列实现,消息队列,商品抢购等等需要有序排队的情况

    set(集合)

  • 描述:
    String 类型的无序集合。成员是唯一的,不能出现重复的数据。

  • 基本用法:
    1、写入:
    SADD set_key val1 [val…]
    SADD set_key val2 [val…]
    2、读取:
    SMEMBERS set_key //读所有合集中的成员
    SCARD set_key //获取集合的成员数
    SREM set_key val2 [val…] //移除集合中一个或多个成员

  • 使用场景:
    例如存一个人的粉丝,交集、并集、差集等操作,可以非常方便的实现如共同关注等功能

    zset(sorted set:有序集合)

  • 描述:
    String 类型的有序集合。不能出现重复的数据,每个元素都会关联一个 double 类型的分数(score),分数可重复

  • 常用命令:
    zadd(key, score, member):向名称为key的zset中添加元素member,score用于排序。如果该元素已经存在,则根据score更新该元素的顺序。
    zrem(key, member) :删除名称为key的zset中的元素member
    zincrby(key, increment, member) :如果在名称为key的zset中已经存在元素member,则该元素的score增加increment;否则向集合中添加该元素,其score的值为increment
    zrank(key, member) :返回名称为key的zset(元素已按score从小到大排序)中member元素的rank(即index,从0开始),若没有member元素,返回“nil”
    zrevrank(key, member) :返回名称为key的zset(元素已按score从大到小排序)中member元素的rank(即index,从0开始),若没有member元素,返回“nil”
    zrange(key, start, end):返回名称为key的zset(元素已按score从小到大排序)中的index从start到end的所有元素
    zrevrange(key, start, end):返回名称为key的zset(元素已按score从大到小排序)中的index从start到end的所有元素
    zrangebyscore(key, min, max):返回名称为key的zset中score >= min且score <= max的所有元素
    zcard(key):返回名称为key的zset的基数
    zscore(key, element):返回名称为key的zset中元素element的score
    zremrangebyrank(key, min, max):删除名称为key的zset中rank >= min且rank <= max的所有元素
    zremrangebyscore(key, min, max) :删除名称为key的zset中score >= min且score <= max的所有元素

  • 使用场景:
    例如排行榜

  • 跳表(集合对象的一种编码结构,skiplist,还有一种ziplist)
    1、元素数量小于128 、所有member长度都小于64 使用ziplist
    2、数据量大时针对redis的有序集合和zset的底层依赖是跳表实现的
    3、zset支持快速的插入和删除 首选链表
    4、zset支持范围查询:让链表排序,但是查找的时间复杂度O(n) 效率不高,要满足范围查询的话效率也比较低
    5、为了效率为链表增加索引,如果只增加一层索引的话数据量大的时候效率还是很慢,所以要增加多级索引
    6、所以这种链表增加多级索引的结构就是跳表
    7、 结构上来讲:跳表在原有的链表上每两个增加一个一个指针
    LIYmGu
    8、根据多层链表的想法可以在继续增加层数,增加节点和节点指针的链接的跨度,类似二分查找
    vRYN3z
    qtUmEe
    9、为了解决插入会打乱链接结构问题问题,在插入时会随机出来一个要插入的层数,这样新插入的节点不会影响其他节点的层数,只需要修改新节点的前后指针
    ZirHwu

    二、缓存穿透、缓存击穿、缓存雪崩区别

    1、缓存穿透(缓存和数据库中都没有的数据):

  • 描述:
    缓存和数据库中都没有的数据,而用户不断发起请求。常规情况存储层查不到数据则不写入缓存,导致这个不存在的数据每次请求都要到存储层去查询

  • 解决:
    1)增加查询参数校验,避免一些超过常规情况和非法的参数请求进来,例如(id:-1)
    2)针对在数据库中找不到记录的,仍然将该空数据存入缓存中,设置一个较短的过期时间,如30秒(设置太长会导致正常情况也没法使用)
    3)布隆过滤器(Bloom Filter),可以快速确定数据是否存在

    2、缓存击穿(缓存没有数据和数据库中有数据,热门key,一般是缓存时间到期):

  • 描述:
    缓存击穿表示某个key的缓存非常热门,有很高的并发一直在访问,如果该缓存失效,那同时会走数据库,压垮数据库。

  • 解决:
    1、让该热门key的缓存永不过期。
    2、增加根据key增加互斥锁,通过redis的setnx实现互斥锁

    3、缓存雪崩(大面积缓存同时到期)

  • 描述:
    大面积缓存同时到期,导致大量请求来访问数据库

  • 解决:
    1、设置缓存过期时间时加上一个随机值,避免缓存在同一时间过期

    三、HyperLogLog使用与应用场景

    1、HyperLogLog:

  • 用来做基数统计的算法,结果有 0.81%的误差,和bitmap相比更节省内存

  • bitmap用来数组来表示各元素是否出现

  • HyperLogLog基数统计的算法(去重后的次数),非redis独有

  • 占用内存小

  • hyperloglog key占用了12K的内存用于标记基数

    2、基本使用

  • pfadd 添加 :pfadd m1 1 2 3 4 1 2 3 2 2 2 2

  • pfcount 获得基数值: pfcount m1 //4

  • pfmerge 合并多个key:
    pfadd m1 1 2 3 4 1 2 3 2 2 2 2 //1
    pfcount m1 //4
    pfadd m2 3 3 3 4 4 4 5 5 5 6 6 6 1 //1
    pfcount m2 //5
    pfmerge mergeDes m1 m2 //ok
    pfcount mergeDes //6

    2、使用场景

  • 需要统计的数据基数很大

  • 可以和bitmap配合使用,bitmap标示存在的元素,hyperloglog用来计数

  • 统计注册IP数、在线人数、每日访问的uv、ip等

    四、redis的集群高可用方案

    1、主从模式:

    一个master可以拥有多个slave,但是一个slave只能对应一个master
    主可以读写,从只能读

  • 缺点:
    主库挂了以后服务将不能写入

    2、Sentinel模式(哨兵):

    建立在主从模式的基础上,监听redis的mater的状态,如果master挂掉了,会在slave上选出一台机器作为master
    使用时需要连接sentinel的ip和port而不是redis的

3、Cluster模式:

在需要更大规模的存储的时候主从模式或sentinel模式就不能很好的满足需求了
cluster模式的解决单机Redis容量有限的问题,将Redis的数据根据一定的规则分配到多台机器
redis版本低的话需要安装对应版本的ruby
去中心化的,每个节点(master)都是平等的,连接哪个节点都可以获取和设置数据。

四、内存回收机制

1、内存使用达到maxmemory上限时候触发的溢出回收策略

  • noeviction(默认): 不会删除任何数据,拒绝所有写入操作并返 回客户端错误信息,此 时Redis只响应读操作。
  • volatile-lru: 根据LRU算法删除设置了超时属性(expire)的键,直到腾出足够空间为止。如果没有可删除的键对象,回退到noeviction策略。
  • volatile-ttl: 根据键值对象的ttl属性,删除最近将要过期数据。如果没有,回退到noeviction策略。
  • allkeys-lru: 根据LRU算法删除键,不管数据有没有设置超时属性,直到腾出足够空间为止。
  • volatile-random: 随机删除过期键,直到腾出足够空间为止。
  • allkeys-random: 随机删除所有键,直到腾出足够空间为止。

*** volatile-xxx 策略只会针对带过期时间的 key 进行淘汰,allkeys-xxx 策略会对所有的 key 进行淘汰

2、删除过期时间的键对象

  • 惰性删除 : 当用户访问已经过期的对象的时候才删除
  • 定时任务删除 : 内部维护了一个定时任务,默认每秒运行10次
    1)定时任务在每个数据库空间采用慢模式随机检查20个键,当发现过期时,删除对应的键。
    2)如果超过检查数25%的键过期,循环执行回收逻辑直到不足25%或 运行超时为止,慢模式下超时时间为25毫秒。
    3)如果之前回收键逻辑超时,则在Redis触发内部事件之前再次以快模式运行回收过期键任务,快模式下超时时间为1毫秒且2秒内只能运行1次。
    4)快慢两种模式内部删除逻辑相同,只是执行的超时时间不同

五、其他知识点汇总

1、redis实现延时队列
使用有序合集,使用时间戳做score, 消息内容作为 key
消费者使用 zrangbyscore 获取当前时间之前的数据做轮询处理。

mysql知识点整理(持续更新)

mysql

一、锁相关

1、悲观锁:

  • 悲观的认为每次操作数据的时候都会有人来修改数据,所以使用数据时对其加锁,确保在自己使用的过程中数据不会被别人修改,使用完成后进行数据解锁。由于数据进行加锁,期间对该数据进行读写的其他线程都会进行等待。写入频繁使用悲观锁

    2、乐观锁:

  • 所以每次获取数据的时候都不会进行加锁,但是在更新数据的时候需要判断该数据是否被别人修改过。一般是在sql的where上带版本号或时间戳,写代码时候自己实现,读取频繁使用乐观锁

    3、共享锁:(自己读写,其他读)

  • 如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。

    4、排他锁:(只能自己读写)

  • 如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。

    5、简要说明为什么会发生死锁?

  • 若干事务相互等待释放封锁,就陷入无限期等待状态,系统就进入死锁

    6、解决死锁的方法应从预防和解除的两个方面着手:

  • 死锁的预防方法:
    ①要求每一个事务必须一次封锁所要使用的全部数据(要么全成功,要么全不成功)
    ②规定封锁数据的顺序,所有事务必须按这个顺序实行封锁。
  • 允许死锁发生,然后解除它,如果发现死锁,则将其中一个代价较小的事物撤消
    ,回滚这个事务,并释放此事务持有的封锁,使其他事务继续运行。

二、事务隔离级别

1、Read uncommitted(读未提交)(脏读):

  • 在该隔离级别,所有事务都可以看到其它未提交事务的执行结果。可能会出现脏读。

    2、Read Committed(读已提交,简称: RC)(幻读):

  • 一个事务只能看见已经提交事务所做的改变。因为同一事务的其它实例在该实例处理期间可能会有新的 commit,所以可能出现幻读。

    3、Repeatable Read(可重复读,简称:RR)(默认,结果相同):

  • 这是 MySQL 的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。消除了脏读、不可重复读,默认也不会出现幻读。

    4、Serializable(串行):

  • 这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。

    三、索引

    摘自

    1、重要概念

  • 基数:单个列唯一键(distict_keys)的数量叫做基数。
  • 回表:当对一个列创建索引之后,索引会包含该列的键值及键值对应行所在的rowid。通过索引中记录的rowid访问表中的数据就叫回表。回表次数太多会严重影响SQL性能,如果回表次数太多,就不应该走索引扫描,应该直接走全表扫描。
    EXPLAIN命令结果中的Using Index意味着不会回表,通过索引就可以获得主要的数据。Using Where则意味着需要回表取数据。

2、优化原则

  • 适合索引的列是出现在where子句中的列,或者连接子句中指定的列;
  • 基数- 较小的类,索引效果较差,没有必要在此列建立索引;
  • 使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间;
  • 不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。

    四、b-tree索、b-tree索引、hash索引

    1、b+tree

  • B+ 树是 B 树的变体,定义基本与 B 树一致,与 B 树的不同点
  • 所有叶子节点中包含了全部关键字的信息
  • 非叶子节点上只存储 key 的信息,这样相对 B 树,可以增加每一页中存储 key 的数量
  • B 树是纵向扩展,最终变成一个 “瘦高个”,而 B+ 树是横向扩展的,最终会变成一个 “矮胖子”
  • 各叶子节点用指针进行连接
  • mysql使用b+树索引
  • innodb索引和数据存在一个文件中
  • myisam下索引叶子节点保存数据的地址而不是数据本身

    2、b-tree

  • 多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点;
  • 所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中;
  • 树的每个节点都包含key值和data值,叶子节点可以看作外部节点,不包含任何信息;
  • 如果 data 比较大时,每一页存储的 key 会比较少;当数据比较多时,同样会有:“要经历多层节点才能查询在叶子节点的数据” 的问题。

    3、mysql中的b+tree

  • 在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;
  • 主聚集索引(clustered index)和辅助索引(secondary index)
  • 具体行数据在在聚集索引上,辅助索引叶子节点存储聚集索引的key值,也就是主见
  • 辅助索引的叶子节点并不包含行记录的全部数据
  • 树的磁盘读写代价更低
  • 树的查询效率更加稳定

    4、B*树:

  • 在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3;

    5、mysql中的hash 索引

  • Hash索引仅仅能满足“=”,“IN”,“<=>”查询,不能使用范围查询
  • 联合索引中,Hash索引不能利用部分索引键查询
  • Hash索引无法避免数据的排序操作
  • Hash索引任何时候都不能避免表扫描
  • Hash索引遇到大量Hash值相等的情况后性能并不一定会比BTree高

五、分库、分表

1、为什么要分表:

  • 减小数据库的单表负担,缩短查询时间,减少锁等待的情况。

    表垂直分区

  • 减少一个表中的字段,根据id关联起来

    表水平分区

  • 使用规则(例如按id哈希、id区间、时间)分组,可以单库多表或多库多表

    分库分表后查询问题

  • 单库多表
    如果分表为myisam引擎的表,可以创建一个merge引擎的集合表来查询(merge引擎要求被合并表中不能又重复字段)
    如果是innodb引擎,只能根据规则单独查询在合并或使用搜索引擎解决
  • 多库多表
    将数据通过同步插件(例如logstash)或者mq同步到Elasticsearch,然后通过lasticsearch查询,
    如果数据量过大可以引入hbase 将数据存到hbase中,然后通过lasticsearch存储hbase的rowkey,然后查到rowkey去hbase中取数据

六、引擎

1、innodb:

  • MySQL 5.5 之后的默认存储引擎
  • 默认引擎
  • 支持行锁
  • 索引叶子节点存储数据
  • 支持事务
  • 默认select不加锁,UPDATE、INSERT、DELETE默认都是排它锁,可以通过(lock in share mode)和(for update)加共享锁和排他锁:
  • Record Lock:单个记录上的索引加锁,Gap Lock:间隙锁,对索引项之间的间隙加锁,但不包括记录本身,Next-Key Lock:Gap Lock + Record Lock,锁定一个范围,并且锁定记录本身。
  • 如果不通过索引条件检索数据,那么 InnoDB 将对表中所有记录加锁,实际效果跟表锁一样(where不命中索引就锁表)。
  • 不擅长快速插入(插入前要排序,消耗时间)
  • .ibd文件存放索引和数据

    2、myisam:

  • MySQL 8.0弃用
  • MySQL 5.5 之前的默认存储引擎
  • 支持表锁
  • 不支持行锁
  • 不支持事务
  • 索- 引叶子节点只保存数据的地址而不是数据本身

七、explain

摘自

1、用途

  • 表的读取顺序如何
  • 数据读取操作有哪些操作类型
  • 哪些索引可以使用
  • 哪些索引被实际使用
  • 表之间是如何引用
  • 每张- 表有多少行被优化器查询
    ……

    2、explain包含的字段

  • id //select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
  • select_type //查询类型
  • table //正在访问哪个表
  • partitions //匹配的分区
  • type //访问的类型
  • possible_keys //显示可能应用在这张表中的索引,一个或多个,但不一定实际使用到
  • key //实际使用到的索引,如果为NULL,则没有使用索引
  • key_len //表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度
  • ref //显示索引的哪一列被使用了,如果可能的话,是一个常数,哪些列或常量被用于查找索引列上的值
  • rows //根据表统计信息及索引选用情况,大致估算出找到所需的记录所需读取的行数
  • filtered //查询的表行占表的百分比
  • Extra //包含不适合在其它列中显示但十分重要的额外信息
    explain图

八、优化方向

1、正确的字段类型和长度
2、选择何时的存储引擎
3、推荐使用utf8mb4字符集,可以存表情,可以表示4个字节宽位,排序规则上general_ci 更快,unicode_ci 更准确。ci不区分大小写,cs区分大小写
4、少用多表操作(子查询,联合查询),而是将SQL拆分多次执行。如果查询很很小,会增加查询缓存的利用率。
5、慢sql查询处理
定位执行较慢的查询语句
show variables like “slow_query%”;
show variables like “%long_query%”;
开启日志
set global slow_query_log=1;
set long_query_time=0.5;
6、尽量不要出现null值 给默认值 (null不会走索引)
7、索引建立
8、使用explain检查sql的执行情况
9、开启表缓存 query_cache_type=On|1 ,查看show variables like ‘%query_cache%’;
10、主从配置
11、分库,分表
12、查看数据库链接池

九、主从同步延时问题

MySQL数据库主从同步延迟是怎么产生的。

主服务器何以并发, 但是从服务器的里面读取binlog 的线程仅有一个, 当某个SQL在从服务器上执行的时间稍长 或者由于某个SQL要进行锁表就会导致,主服务器的SQL大量积压,未被同步到从服务器里。这就导致了主从不一致, 也就是主从延迟。

如何查看是否存在延迟

1、可以通过监控show slave status\G命令输出的Seconds_Behind_Master参数的值来判断,是否有发生主从延时。:
NULL - 表示io_thread或是sql_thread有任何一个发生故障,也就是该线程的Running状态是No,而非Yes.
0 - 该值为零,是我们极为渴望看到的情况,表示主从复制良好,可以认为lag不存在。
正值 - 表示主从已经出现延时,数字越大表示从库落后主库越多。
负值 - 几乎很少见,这是一个BUG值,该参数是不支持负值的,也就是不应该出现。

MySQL数据库主从同步延迟解决方案。

1、slave的sync_binlog设置为0或者关闭binlog:

  • sync_binlog 表示每sync_binlog次事务提交,MySQL调用文件系统的刷新操作将缓存刷下去,1最安全
    2、使用比主库更好的硬件设备作为slave。
    3、slave的innodb_flush_log_at_trx_commit 设置0:
  • 默认值1的意思是每一次事务提交或事务外的指令都需要把日志写入(flush)硬盘
  • 设置2的意思是不写入硬盘而是写入系统缓存。日志仍然会每秒flush到硬盘,只会在整个操作系统 挂了时才可能丢数据,一般不会丢失超过1-2秒的更新
  • 设成0会更快一点,但安全方面比较差,即使MySQL挂了也可能会丢失事务的数据。
    4、–logs-slave-updates 从服务器从主服务器接收到的更新不记入它的二进制日志

php版bitmap

###二进制转十进制
32位机器上的自然数一共有4294967296个,
如果用一个bit来存放一个整数,1代表存在0代表不存在,那么把全部自然数存储在内存只要4294967296 / (8 * 1024 * 1024) = 512MB(8bit = 一个字节)
而这些自然数存放在文件中,一行一个数字,需要20G的容量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
     
class BitMap
{
public static $map = [];
//插入值
public static function setValue($value) {
$index = self::getIndex($value);
if (isset(self::$map[$index])) {
self::$map[$index] |= 1 << ($value & 31);
} else {
self::$map[$index] = 1 << ($value & 31);
}
/* (1) $value & 31 相当于 $value % 32 ,
* 这个是取当前的值标示是否存在的位置
* 例如$value = 67 得到3 位置应该是在二进制从右往左数的下标3的位置也就是第四位
* (2) 1 << ($value & 31) 就是 把当前得到的值转化成二进制的位置
* 例如$value = 67 得到3 3的二进制 为11 向左偏移3位 得到2进制 1000
* (3) |= 是 按位或运算并赋值
* 所以当前$map[$index] 的二进制的值为 1000 十进制就是8
*/


echo "当前值的二进制:".(decbin($value))."\n";
echo "取模位置左移1:".(decbin(1 << ($value & 31))).'-'.(1 << ($value & 31))."\n";
echo "插入后map:".(decbin(self::$map[$index])).'-'.(self::$map[$index])."\n";
}
//查找某个值是否存在
public static function haxValue($value) {
$index = self::getIndex($value);
if (!isset(self::$map[$index])) {
return false;
}
/**
* 例如传入的值为87,bitmap中存的值为67
* (1) 1 << ($value & 31) 得到1<<23 ,1左偏移23位,
* 得到 二进制值为 100000000000000000000000 也就是十进制的8388608
* (2) 然后和 self::$map[$index] 进行按位与(二进制1000 和 二进制 100000000000000000000000)
* 得到二进制 000000000000000000000000也就是十进制的0 也就是不存在
* 如果存在则返回这个数在self::$map[$index] 中的位置转换成的十进制的值 (如67返回8二进制也就是1000)
*/
if ((self::$map[$index] & (1 << ($value & 31)))==0) {
return false;
}
return true;
}

public static function display()
{
$keys = array_keys(self::$map);
foreach ($keys as $key) {
print "map index: {$key}, bit:\n";
$temp = self::$map[$key];
$bit_str = '';
for ($i = 0; $i <= 31; $i++) {
$bit_str = (1 & $temp) . $bit_str;
echo ($bit_str)."\n";
$temp >>= 1;
}
echo "{$bit_str}\n";
}

}
//去出在数组的第几个key里面
private static function getIndex($value) {
return $value >> 5;
/**
* 相当于floor($value / 32);
* 这里是2进制运算当前值右移动5位
* 例如67 二进制为1000011 右移动5位二进制为10 十进制为2
*/
}
}


$list = [67];
foreach ($list as $value) {
BitMap::setValue($value);
}

BitMap::display();
var_dump(BitMap::haxValue(87));

2进制10进制转换

###二进制转十进制

1
2
3
4
例:二进制110101转10进制
= 1 * 2^5 + 1 * 2^4 + 0 * 2^3 + 1 * 2^2 + 0 * 2^1 + 1 * 2^0
= 32 + 16 + 0 + 4 + 0 + 1
= 53

上面是2进制转10进制所以是用2的次方 , 如果3制转10进制那就用3的次方

###十进制转2进制

1
2
3
4
5
6
7
8
例:十进制53转2进制 
= 53/2 = 26 余1
= 26/2 = 3 余0
= 13/2 = 6 余1
= 6/2 = 3 余0
= 3/2 = 1 余1
= 1/2 = 0 余1
然后余数倒序 = 110101

上面是转2进制所以除2 , 如果转3进制就除3 ,4进制就除4

HTTP状态码

HTTP状态码(HTTP Status Code)是用来表示网页服务器HTTP 响应状态的3位数字代码。它由RFC 2616规范定义的,并得到RFC 2518、RFC 2817、RFC 2295、RFC 2774、RFC 4918等规范扩展。所有状态码的第一个数字代表了响应的五种状态之一

HTTP/1.1定义的状态码值和对应的原因短语(Reason-Phrase)的例子。
1XX表示:消息
这一类型的状态码,代表请求已被接受,需要继续处理。这类响应是临时响应,只包含状态行和某些可选的响应头信息,并以空行结束。由于HTTP/1.0 协议中没有定义任何1xx状态码,所以除非在某些试验条件下,服务器禁止向此类客户端发送1xx响应。 这些状态码代表的响应都是信息性的,标示客户应该采取的其他行动。
100 : Continue 客户端应当继续发送请求。这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分.
101 : witching Protocols 服务器已经理解了客户端的请求,并将通过Upgrade消息头通知客户端采用不同的协议来完成这个请求。在发送完这个响应最后的空行后,服务器将会切换到 在Upgrade消息头中定义的那些协议。: 只有在切换新的协议更有好处的时候才应该采取类似措施.
102: Processing 由WebDAV(RFC 2518)扩展的状态码,代表处理将被继续执行。
2XX表示:成功
这一类型的状态码,代表请求已成功被服务器接收、理解、并接受。
200 : OK
201 : Created 已创建
202 : Accepted 接收
203 : Non-Authoritative Information 非认证信息
204 : No Content 无内容
205 : Reset Content 重置内容
206 : Partial Content 服务器已经成功处理了部分GET请求。类似于FlashGet或者迅雷这类的HTTP 下载工具都是使用此类响应实现断点续传或者将一个大文档分解为多个下载段同时下载。
207: Multi-Status 由WebDAV(RFC 2518)扩展的状态码,代表之后的消息体将是一个XML消息,并且可能依照之前子请求数量的不同,包含一系列独立的响应代码.
3XX表示: 重定向
这类状态码代表需要客户端采取进一步的操作才能完成请求。通常,这些状态码用来重定向,后续的请求地址(重定向目标)在本次响应的Location域中指明。
300 : Multiple Choices 多路选择
301 : Moved Permanently 永久转移
302 : Found 暂时转移
303 : See Other 参见其它
304 : Not Modified 未修改
305 : Use Proxy 使用代理
306: Switch Proxy 在最新版的规范中,306状态码已经不再被使用。
307 : Temporary Redirect 请求的资源现在临时从不同的URI响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或 Expires中进行了指定的情况下,这个响应才是可缓存的。
4XX表示: 客户端错误
这类的状态码代表了客户端看起来可能发生了错误,妨碍了服务器的处理。
400 : Bad Request 错误请求
401 : Unauthorized 未认证
402 : Payment Required 需要付费
403 : Forbidden 禁止
404 : Not Found 请求失败,请求所希望得到的资源未被在服务器上发现
405 : Method Not Allowed 方法不允许
406 : Not Acceptable 不接受
407 : Proxy Authentication Required 需要代理认证
408 : Request Time-out 请求超时
409 : Conflict 冲突
410 : Gone 失败
411 : Length Required 需要长度
412 : Precondition Failed 条件失败
413 : Request Entity Too Large 请求实体太大
414 : Request-URI Too Large 请求URI太长
415 : Unsupported Media Type 不支持媒体类型
416 : Requested range not satisfiable 如果请求中包含了Range请求头,并且Range中指定的任何数据范围都与当前资源的可用范围不重合,同时请求中又没有定义If-Range请求头,那 么服务器就应当返回416状态码。
417 : Expectation Failed 在请求头Expect中指定的预期内容无法被服务器满足
421 :There are too many connections from your internet address 从当前客户端所在的IP地址到服务器的连接数超过了服务器许可的最大范围。通常,这里的IP地址指的是从服务器上看到的客户端地址.
422: Unprocessable Entity 请求格式正确,但是由于含有语义错误,无法响应。(RFC 4918 WebDAV)
423: Locked 当前资源被锁定。(RFC 4918 WebDAV)
424: Failed Dependency 由于之前的某个请求发生的错误,导致当前请求失败,例如PROPPATCH。(RFC 4918 WebDAV)
425: Unordered Collection 在WebDav Advanced Collections草案中定义,但是未出现在《WebDAV顺序集协议》(RFC 3658)中。
426:Upgrade Required 客户端应当切换到TLS/1.0。(RFC 2817)
449: Retry With 由微软扩展,代表请求应当在执行完适当的操作后进行重试。
5XX表示: 服务器错误
这类状态码代表了服务器在处理请求的过程中有错误或者异常状态发生
500 : Internal Server Error 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器的程序码出错时出现
501 : Not Implemented 未实现
502 : Bad Gateway 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应
503 : Service Unavailable 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。
504 : Gateway Time-out 网关超时
505 : HTTP Version not supported 服务器不支持,或者拒绝支持在请求中使用的HTTP版本。这暗示着服务器不能或不愿使用与客户端相同的版本。响应中应当包含一个描述了为何版本不被支持以及服务器支持哪些协议的实体。
506 : Variant Also Negotiates 由《透明内容协商协议》(RFC 2295)扩展,代表服务器存在内部配置错误:被请求的协商变元资源被配置为在透明内容协商中使用自己,因此在一个协商处理中不是一个合适的重点。
507 : Insufficient Storage 服务器无法存储完成请求所必须的内容。这个状况被认为是临时的。WebDAV(RFC 4918)
509 : Bandwidth Limit Exceeded 服务器达到带宽限制。这不是一个官方的状态码,但是仍被广泛使用。
510 : Not Extended 获取资源所需要的策略并没有没满足。

css去除webkit内核的默认样式

##前言
做移动端的朋友应该知道,iphone的默认按钮是个很恶心的圆角,select下拉框也有默认样式无法修改。

###解决办法
这时候可以用到的
在按钮和select样式里加入下面css
–webkit–appearance:none //去除默认样

###示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
input{
background:#FF9933;
border–radius:5px;
color:#fff;
padding:3px 20px;
–webkit–appearance:none;
}
select{
background:url(images/img1.jpg) no–repeat;
background–size:20px;
background–position:95% center;
padding:3px 10px;
width:30%;
–webkit–appearance:none;
}

MySQL修改root密码的几种方法

方法1: 用SET PASSWORD命令

首先登录MySQL。

格式:mysql> set password for 用户名@localhost = password(‘新密码’);

例子:mysql> set password for root@localhost = password(‘admin100’);

上面例子将用户root的密码更改为admin100

方法2:用mysqladmin

格式:mysqladmin -u用户名 -p旧密码 password 新密码

例子:mysqladmin -uroot -p123456 password admin100

上面例子将用户root原来的密码123456改为新密码admin100

方法3:用UPDATE直接编辑user表

首先登录MySQL。

mysql> use mysql;

mysql> update user set password=password(‘admin100’) where user=’root’ and host=’localhost’;

mysql> flush privileges;

方法4:在忘记root密码的时候,可以这样。

以windows为例:

  1. 关闭正在运行的MySQL服务。

  2. 打开DOS窗口,转到mysql\bin目录。

  3. 输入mysqld –skip-grant-tables 回车。–skip-grant-tables 的意思是启动MySQL服务的时候跳过权限表认证。

  4. 再开一个DOS窗口(因为刚才那个DOS窗口已经不能动了),转到mysql\bin目录。

  5. 输入mysql回车,如果成功,将出现MySQL提示符 >。

  6. 连接权限数据库: use mysql; 。

  7. 改密码:update user set password=password(“admin100”) where user=”root”;(别忘了最后加分号) 。

  8. 刷新权限(必须步骤):flush privileges;。

  9. 退出 quit。

  10. 注销系统,再进入,使用用户名root和刚才设置的新密码admin100登录。