centos7下安装sphinx coreseek 并且自定义分词字典

centos7下安装sphinx coreseek

因为工作上需要,这边需要安装个coreseek本地复现一些线上问题。 以前一直认为coreseek是sphinx下单独的插件,类似es的ik分词器。在实际安装
中发现coreseek是基于sphinx上面套了一层并非插件,我记录下了自己安装coreseek的一些经历和自定义分词字典的步骤,都蛮简单的全是做记录吧。

sphinx 和 elasticsearch 选择

sphinx版本迭代更新慢 基本处于没人维护的状态,搜索和拆词方面相对 没elasticsearch 那么灵活。但是考虑到项目现状我们还是任然在使用sphinx,因为前期sphinx开销小,技术人员相对不用操作复杂的查询语法,个人更喜欢elasticsearch但是现状用sphinx更优,而且我博客的服务器也是因为贫穷只能配置 sphinx

安装

下载

1
2
3
4
wget http://files.opstool.com/man/coreseek-4.1-beta.tar.gz
tar -xzvf coreseek-4.1-beta.tar.gz
cd coreseek-4.1-beta

目录结构

1
2
3
4
5
6
coreseek-4.1-beta
├── csft-4.1 //主控
├── mmseg-3.2.14 //分词器
├── README.txt
└── testpack //拆词包

编译安装mmseg-3.2.14

1
2
3
4
cd mmseg-3.2.14
./configure --prefix=/usr/local/coreseek-4.1
make
make install

ps:如果出现C++ compiler cannot create executables 可以

1
yum install gcc gcc-c++ gcc-g77

ps:如果出现error: cannot find input file: src/Makefile.in可以

1
2
3
4
5
6
aclocal
libtoolize --force
automake --add-missing
autoconf
autoheader
make clean

安装csft-4.1-beta

1
2
3
4
5
6
7
8
9
10
11
12
cd csft-4.1

sh buildconf.sh #输出的warning信息可以忽略,如果出现error则需要解决

./configure \
--prefix=/usr/local/sphinx \
--without-unixodbc \
--with-mmseg \
--with-mmseg-includes=/usr/local/coreseek-4.1/include/mmseg/ \
--with-mmseg-libs==/usr/local/coreseek-4.1/lib/ \
--with-mysql

如果 出现 automake: warnings are treated as errors

1
2
3
4
5
vim configure.ac
13行 >>>
AM_INIT_AUTOMAKE([-Wall -Werror foreign]) 改为
AM_INIT_AUTOMAKE([-Wall foreign]) subdir-objects

继续编译

1
2
make
make install

如果出现 make[2]: *** [sphinxexpr.o] 错误 1

1
2
3
4
5
6
在 csft-4.1/src/sphinxexpr.cpp 文件中, 替换所有

T val = ExprEval ( this->m_pArg, tMatch );

T val = this->ExprEval ( this->m_pArg, tMatch );

目录介绍

coreseek目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
./coreseek
├── bin
│   ├── indexer //创建全文索引
│   ├── indextool
│   ├── search //简单的命令行测试程序
│   ├── searchd //守护进程服务
│   └── spelldump
├── etc
│   ├── example.sql //测试文件
│   ├── sphinx.conf //配置文件
│   ├── sphinx.conf.dist
│   └── sphinx-min.conf.dist
├── share
│   └── man
└── var
├── data //数据文件
└── log //日志

MMSeg 目录

1
2
3
4
5
6
7
8
9
10
11
12
coreseek-4.1
├── bin
│   └── mmseg //分词器
├── etc
│   ├── mmseg.ini //使用分词词库
│   ├── unigram.txt //词库的文本文件
│   └── uni.lib
├── include
│   └── mmseg
└── lib
├── libmmseg.a
└── libmmseg.la

配置

/usr/local/coreseek/etc加入sphinx.conf写入内容
详细的字段介绍需要对接文档,下方会给出

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
source src1
{
type = mysql
sql_host = localhost
sql_user = root
sql_123 =
sql_db = blog
sql_query_pre= SET NAMES utf8

sql_query = \
SELECT id as cid,title,data FROM zjj_articles
sql_attr_uint = cid


sql_ranged_throttle = 0
}
source src1throttled : src1
{
sql_ranged_throttle = 100
}
index test1
{
source = src1
path = /usr/local/coreseek/var/data/test1
docinfo = extern
mlock = 0
morphology = none
min_word_len = 1
charset_type = zh_cn.utf-8
charset_dictpath = /usr/local/coreseek-4.1/etc/
html_strip = 0
}

indexer
{
mem_limit = 32M
}
searchd
{
listen = 9312

log = /usr/local/coreseek/var/log/searchd.log
query_log = /usr/local/coreseek/var/log/query.log
read_timeout = 5
client_timeout = 300
max_children = 30
pid_file = /usr/local/coreseek/var/log/searchd.pid
max_matches = 1000
seamless_rotate = 1
preopen_indexes = 1
unlink_old = 1
mva_updates_pool = 1M
max_packet_size = 8M
max_filters = 256
max_filter_values = 4096
max_batch_queries = 32
}

启动

1.生成索引文件

1
/usr/local/coreseek/bin/indexer -c /usr/local/coreseek/etc/sphinx.conf --all --rotate option

2.启动服务器

1
/usr/local/coreseek/bin/searchd -c /usr/local/coreseek/etc/sphinx.conf

自定义字典

有的时候我们需要自定义词典,这时候就需要修改mmseg目录下的文件了。

1
2
3
4
5
6
7
/mmseg/etc

etc/
├── mmseg.ini
├── unigram.txt //未编译的词典
└── uni.lib //编译后的字典

我们先测试下 张俊杰是大帅哥 这个词组

张俊杰是大帅哥

可以看见分词结果没把 张俊杰 ,大帅哥,帅哥等词单独拆出来。我们自定义下词典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1.备份已有文件
cp uni.lib uni.lib.pak

2.修改unigram.txt,加入(unigram.txt开始时候不能有x:1 否则会出现) `Segmentation fault (core dumped)`

张俊杰 1
x:1
大帅哥 1

3.生成字典
/usr/local/mmseg3/bin/mmseg -u /usr/local/mmseg3/etc/unigram.txt

4.替换掉字典
mv unigram.txt.uni uni.lib

测试了下不用重启Sphinx,顺利分词

索引选择与查询语句优化

#索引选择

MySQl在一张表中有多个索引,在我们书写查询语句的时候没有指定使用哪个索引来进行查询,这部分操作是由MySQL的查询优化器来决定的。但是查询优化器有选择错误的情况,常见的几种如下(丁奇大佬列举的列子我只是做了归纳整理)

优化器的逻辑

选择索引是优化器的工作,优化器的目的是找到最优的执行方案主要影响索引选择的因素如下几个

  • 扫描行数
  • 是否临时表
  • 是否排序
  • 是否回表

基础表

表结构

1
2
3
4
5
6
7
8
9
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `a` (`a`),
KEY `b` (`b`)
) ENGINE=InnoDB;

数据填充

1
2
3
4
5
6
7
8
9
10
11
12
13
delimiter ;;
create procedure idata()
begin
declare i int;
set i=1;
while(i<=100000)do
insert into t values(i, i, i);
set i=i+1;
end while;
end;;
delimiter ;
call idata();

扫描行数判断

MySQL在执行语句时不能精确的知道满足记录的条数(影响行),而是进行统计估算的方式。统计信息就是索引的区分度,在索引上不同的值越多区分度越好,索引不同值的个数叫基数。也就是说基数越大索引区分度越好。

索引基数查看

1
show index from t;

索引基数查看

索引基数采用采样统计的方法,InnoDB默认选择N个数据页,统计这些页上的不同值,得到平均值然后乘索引的页面数。

案例一

这个案例讲到是错误的影响行数诱导索引优化器进行全表扫描,表结构和数据如上述。我们先执行下查询语句然后进行如下操作

1
EXPLAIN SELECT *FROM t WHERE a BETWEEN 10000 AND 20000;

查询计划

可以注意到我们查询t表的 1W - 2W 区间 用到索引a 并且影响 1W行。

下面我们按需进行如下操作

session A session B
start transaction with consistent snapshot;
delete from t;
call idata();
explain select*from t where a between 10000 and 20000;
commit;

start transaction with consistent snapshot; 会在语句执行时就启动事务,这个案例中我反复试验得出的结果如下

查询计划

虽然我在本机没有试出想要的未提交事务干扰查询优化器进行影响行数判断,但是这种情况的确存在,初步猜想是试验中的MySQL版本对这块查询优化器做了升级MySQL5.6.43。对于这种cardinality大于或者小于实际数值的情况我们一般用 analyze table t 来进行优化

show index

ps:理论上来说cardinality应该大于实际值,这边反而小于。猜想这是试验不成功的原因

analyze 后

analyze table t

案例二

这个案例讲的是优化器误判的情况,表结构和数据依然如上。

1
explain select * from t where (a between 1 and 1000)  and (b between 50000 and 100000) order by b limit 1;

上述语句我们要查询 a 1-1K 范围内的数据 和 b 在 5W-10W范围内的数据 并且按照b 升序排序取第一条。人肉分析一波在这个SQL下去后执行的过程

  • 如果使用索引a,会先查到前1000个满足条件的数据,然后回表到主键上找到相应的b值查看是否满足条件。

  • 如果使用索引b,会找到50000个满足条件的数据,然后回表到主键上找到相应的a值是否满足条件。

我们认为索引选择a是最优的,但是在实际的操作过程中会出现如下情况

mysql选错索引了

为什么会这样呢?因为后面还有个 order by b limit 1 这样选择b就可以避免排序(b本身是索引已经有排序了)。如果 limit 1 大于了 a between 1 and 1000 优化器会怎么做呢?

limit 1001

优化

  • 强制指定索引 force index(a)
1
explain select * from t force index(a) where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;

force index(a)

  • 把a加入排序
1
explain select * from t where (a between 1 and 1000) and (b between 50000 and 100000) order by b,a limit 1;

a加入排序

  • 删除索引b
1
2
drop index b on t;
explain select * from t where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;

删除索引b

mysql 的最左前缀原则

在MYSQL InnoDB 中 联合索引的最左原则是一块比较常用的概念,在百度搜索了很多帖子或多或少都和实际情况优先出入,大多是基于《高性能MYSQL》上进行的总结文章,但是时隔多年在实际使用环境下难免有一些版本升级带来的优化问题。带着这些问题进行了下面一波小实验。

联合索引

###优点

  • 覆盖索引,这一点是最重要的,重所周知非主键索引会先查到主键索引的值再从主键索引上拿到想要的值,这样多一次查询索引下推。但是覆盖索引可以直接在非主键索引上拿到相应的值,减少一次查询。

  • 在一张大表中如果有 (a,b,c)联合索引就等于同时加上了 (a) (ab) (abc) 三个索引减少了存储上的一部分的开销和操作开销

  • 梯度漏斗,比如 select *from t where a = 1 and b = 2 and c = 3; 就等于在满足 a = 1 的一部分数据中过滤掉 b = 2 的 再从 a = 1 and b = 2 过滤掉 c = 3 的,越多查询越高效。

最左前缀原则

B+ 最左前缀

ps:上图转载极客时间

从上图可以看到联合索引其实是利用的B+树这种数据结构,比如单独查询 “王五” 和 “王%” 其实都可以用到最左边的值。但是直接查询年龄就会用不到这个联合索引。类似于给(a,b,c)加上联合索引就等于同时加上了 (a) (ab) (abc)但是不能丢下a单独来使用。

where条件的顺序

在很多文章,或者在面试中很多候选人对这一块都是没有一个统一答案的。有的认为顺序会有影响因为这是最左原则有的认为顺序没有影响,mysql的查询优化器会来进行排序这件事。下面做个小的实验来证明下上面2个观点

实验

表结构

1
2
3
4
5
6
7
8
CREATE TABLE `ttt` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `test` (`a`,`b`,`c`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8

伪造数据

1
2
3
4
5
6
7
8
9
10
11
12
13
delimiter ;;
create procedure idata()
begin
declare i int;
set i=1;
while(i<=100000)do
insert into ttt values(i, i, i, i);
set i=i+1;
end while;
end;;
delimiter ;
call idata();

测试 (以下结果在mysql5.6.43 进行)

1
2
mysql> explain select *from ttt where a = 1 ;

联合查询,前缀原则

1
mysql> explain select *from ttt where a = 1 and b = 1;

联合查询,前缀原则a=1 and b =1

1
mysql> explain select *from ttt where a = 1 and b = 1 and c =1;

联合查询前缀原则 最左原则 a=1 and b=1 and c=1

1
mysql> explain select *from ttt where  b = 1 and c =1;

联合查询 前缀原则 最左原则 b=1 and c=1

1
mysql> explain select *from ttt where  b = 1 and a =1;

联合查询 前缀原则 最左原则 b = 1 and a =1

1
explain select *from ttt where  b = 1 and a =1 and c = 1

联合查询 前缀原则 最左原则 b = 1 and a =1 and c = 1

为排除查询缓存的可能SQL_NO_CACHE 在核对次结果

1
explain select SQL_NO_CACHE *from ttt where  b = 1 and a =1 and c = 1;

联合查询 前缀原则 最左原则 关闭缓存

尝试去除中间的索引

1
explain select SQL_NO_CACHE c from ttt where  a = 1 and c =1;

上述我们主要关注2点第一个是 type 和 一个是ref,可以得到下面的结果

where 是否用到联合索引
(a) OK
(a,b) OK
(a,b,c) OK
(b,c) NO
(b,a) OK
(b,a,c) OK
(a,c) 只命中a

总结

联合查询遵循最左前缀原则,最左前缀原则讲究加索引时候的排序,对于搜索中的where在mysql的后续版本中会有查询优化器进行优化在使用时不必强调顺序(但是在一些特定场景下查询优化器可能存在错误判别,并且笔者未考证最左前缀与版本之间的关系所以建议遵守加索引时的顺序)

《非暴力沟通》

《非暴力沟通》介绍的是一种沟通方法,这种沟通方法或者说公式可以让人潜意识的保持冷静。沟通讲的是解决问题而不是矛盾升级或者是否争辩。

非暴力沟通

这是一本在性善论上建立的沟通方式,全书表达了一个沟通模式还算实用 观察 > 感受 > 需要 > 请求

eg

  • 当我想到上班的事情时(观察)我好害怕(感受)因为我要确保孩子能得到很好的照顾(需要)

  • 为照顾孩子,我放弃了工作,留在家里(观察)我有点心灰意冷(感受)因为我看重成就(需要)。我想,我现在或许可以去找份兼职(请求)

书中还强调了一个很重要的沟通技巧倾听(在如《人性的弱点》这样的书中也强调过倾听,由此可见它还是很重要的沟通方式)。

普通索引于唯一索引

##知识点

  • LRU
  • change Buffer
  • buffer pool

查询过程

名称 过程
普通索引 查询第一条满足条件的数据,继续查找直到不满足条件停止
唯一索引 查找到第一条,因为保持了唯一性停止查找

InnoDB 数据读写按照数据页为单位读写,也就是说不是把记录本身从磁盘读出来,而是以页为单位读入内存。对于普通索引来说找到第一条记录它所在的数据页就在内存中了,所以后续消耗不大

更新过程

在更新过程中,如果数据页在内存中就直接更新。如果数据没在内存中,在不影响一致性的前提下,InooDB会将更新操作缓存在 change buffer 中节省磁盘的随机读取,在下次查询访问这个数据页的时候读入内存,在change buffer中执行这个页有关操作保证逻辑正确性。数据从change buffer 中写入内存中主要 会在 查询访问这个数据,数据库关闭或者定期默认merge。

什么索引能用到Change Buffer

唯一索引会验证数据的唯一性这个操作需要把数据读入内存才能判断,所以这种就会直接修改内存页上的数据,写到Change Buffer就多此一举了。所以Change Buffer 只有普通索引可以使用

Buffer Pool

Change Buffer 会把数据临时写入 Buffer Pool。为了提高大容量读取操作效率,Buffer Pool会基于LRU定期刷新数据到磁盘上

选择

普通索引和唯一索引查询效率差不多,考虑对更新性能的影响尽量选择普通索引。当然前提是保证业务的情况下,不是所有业务场景等能靠程序来维护数据唯一性的。在读写密集型的场景下Change Buffer带来的收益并不明显,反而更复杂。在更新操作频繁,查询操作少的情况下Change Buffer就会发挥很大的功效

可参考文档

Change Buffer

Buffer Pool

《奈飞文化手册》

奈飞是一家做的非常不错的公司,流传很广的是它132页的PPT中英标准长下面这个样子。这边书可以说是对这个PPT更深一程度的解读了。

奈飞

我感觉比较重要的是如下几点

  • 招聘成年人
  • 让每个人理解公司业务
  • 高效反馈
  • 事实捍卫观点
  • 团队建设
  • 人才吸引人才

全书也是围绕这几点展开阐述的,主要观点是招聘一批专业技能突出的优秀人才,配合企业文化让大家都有施展才能的空间。文中很多观点都让人向往,甚至迫不及待的想要实施,但是结合大环境我们想实现奈飞这样的企业文化还是会有一段不断改正完善壮大的过程。当下我们可以努力做到下面几点

  1. 上传下达,保持沟通的强节奏
  2. 让每个执行者都知道执行这个任务的意图
  3. 积极收集过滤消化来自上中下的反馈
  4. 招聘对的伙伴

mysql的事务隔离概念

InnoDB默认隔离级别为可重复读,更新数据的时候使用当前读。他们都是在内部视图的基础支持的基础上完成的。下面会记录一些mysql的事务回滚和事务启动时机也记了一些在官方手册上撸下来的概念

#事务隔离

1
2
3
4
5
6
7
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`k` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);

事务A 事务B 事务C
start transaction with consistent snapshot;
start transaction with consistent snapshot;
update t set k=k+1 where id=1;
update t `set k=k+1 where id = 1;
select k from t where id=1;
select k from t where id = 1;
commit;
commit;

事务A显示 1
事务B显示 3

事务的启动时机

上面例子中用到 start transaction with consistent snapshot这和传统的begin/commit最大的区别是,前者事务的启动时机是运行时创建视图执行,后者是begin后的第一条语句执行后创建视图。

MVCC是什么

在mysql官方文档中可以看见

innoDB 是一个多版本储存引擎,它保持行的信息修改版本,它自持事务特性入如并发和回滚。这些信息在表空间名为回滚段的数据结构里(rollback segment)InnoDB使用回滚段中的信息来进行回滚操作,它还保持了这些信息可以恢复到老版本实现一致性读

在内部,InnoDB给每行储存3个字段放在数据库中。

  • DB_TRX_ID(6byte)

插入,更新的最后一次事务标识,删除在内部是一个更新,将特定的哪一行设置删除标识

  • DB_ROLL_PTR(7byte)

回滚指针指向写入回滚段的undo log,如果信息更新undo log 包含所有更改信息

  • DB_ROW_ID(6byte)

写入新行ID

MVCC 全写是 multiversion concurrency control 多版本并发控制。

快照在MVCC的工作原理

InnoDB在事务之间数据切换依靠一个个的快照,这些快照并不是像数据库备份一样全量记录下来而是依靠一些逻辑关系计算得出。上面有讲到DB_TRX_ID,每次数据提交时都会有个新的数据版本写入undo log并且对应了一个trx_id。

eg:

V1 V2 V3 V4
- set k=10 set k=k+1 set k = k*2
k=1 k=10 k=11 k=22
trx_id=10 trx_id=15 trx_id=17 trx_id=25

按照 事务隔离级别可重复读的定义,一个事务启动的时候,能够看到所有已经提交的事务结果,但是之后这个事务执行期间其他事务的更新它不可见。InnoDB为每个事务构建一个数组,用来记录启动了还未提交的所有ID(包括自己),它利用所有数据都有多个版本实现了MVCC这个能力。

视图的能见度可以总结成

  • 版本未提交,不可见
  • 版本已提交,但是在视图创建后提交,同上
  • 版本已提交,而且在视图创建前,可见

更新逻辑

更新数据是先读后写,而这个读,只能读当前值,称为当前读 。可以理解为更新前会实时读取当前字段的数据修改。

《增长黑客》

公司老板推荐的书单,作为互联网创业的成功者推荐的书可以说这本书是相当的优秀了,正确的产品低成本获取用户,病毒式的传播。里面都有一套市面上常见的运营方法

增长黑客

《增长黑客》讲述了大量互联网营销的成功案例分析了其成功的原因,这本书主要围绕

  1. 增长黑客的价值
  2. 最小成本验证市场打磨正确的产品
  3. 常见的获取用户的方法
  4. 激活用户的常见手段
  5. 提高留存的方法
  6. 产品的付费模式
  7. 病毒传播的常见手段

本身职责是技术开发,读这本书的目的是拓宽视野增加是非把控和与其他职能部门更密切的配合(知道需求本身的目的是什么,这样做想要达到什么效果)这本书我印象最深的是

  • 有明确的目的性,你的产品定位是什么?
  • 产品初创,在人力物力不理想的状态尽量低成本试错
  • 第一批种子用户获取最为困难,我们要想办法让他们把产品用起来并且留下来
  • 基于人际关系的产品传播更容易产生裂变,低成本获取更多用户

全局锁,表锁,行锁

锁的范围越大,并发性就越差。但是这并不意味锁定范围做小的锁就好了,技术还是围绕业务场景展开的,这边写出一点对全局锁 表级锁 行锁的理解

全局锁

当需要让整个数据库都处于只读状态的时候,可以使用下面这个命令,之后的其他数据更新,数据定义语句全部会被堵塞住

1
FLUSH TABLE WITH READ LOCK

它的应用场景是做全库的逻辑备份,但是一般没人会真的这样做。因为这样做会有一堆的风险,主库这样做业务GG,从库这样做主从业务延迟。

在讲到事务隔离级别,时有一个可重复读可以解决备份时的逻辑问题,mysql自带的mysqldump工具中加上-single-transaction在导出数据前就会启动一个事务,来确保一致性视图。但是需要注意这个方法只适用支持事务的数据库引擎

表级锁

名称 互斥
读锁 X Y X
写锁 X X Y

表锁

1
2
3
lock tables ... read/write

unlock tables

元素锁

不需要显式操作,在访问一个表的时候自动加上,meatadate lock的作用是保证读写的正确性,在5.5的版本做正删改操作的时候MDL加读锁,对表做结构变更MDL加写锁

行锁

InnoDB的行锁提高了数据库的并发性(锁的范围小),但是一些错误的使用还是会造成致命的后果下面介绍下InnoDB里的二阶段锁协议及如何在事务中更高效的支持并发。

二阶段锁协议

步骤 语句一 语句二
1 BEGIN
2 UPDATE zjj_tags set tid =99 WHERE id = 6032 BEGIN
3 UPDATE zjj_tags set tid =100 WHERE id = 6032
4 UPDATE zjj_tags set tid =1 WHERE id = 6033 堵塞
5 COMMIT 等待
6 id=6032,tid=99 COMMIT
7 id=6032,tid=100

所谓的二阶段锁协议就是把锁分为了加锁和解锁的阶段,从上面的执行表格可以看出语句一在执行时事务未提交,语句二的事务是处于堵塞状态的,这就是所说的二阶段锁协议。对于事务而言,事务开始处于加锁状态事务结束处于解锁状态。只有满足二段锁协议的数据库,并发调度的事务才是可以串行化

优化

通过上述我们知道了二阶段锁协议由此可见如果想最快的提升事务的性能我们可以把容易造成冲突的语句尽量放到事务最后执行,这样可以缓解事务加锁对于并发请求的等待

死锁

死锁就是事务和事务间的依赖造成的死循环,解决死锁的方法有2种

  • 进入死锁等待超时innodb_lock_wait_timeout 默认50秒
  • 发起死锁检查innodb_deadlock_detect 5.7后开始自持默认开启

我们不能手动的吧innodb_lock_wait_timeout调太小,因为我们不知道事务是否会在我们认为的一个理想状态下提交。但是innodb_deadlock_detect有一定的性能消耗。在高并发的环境下我们可以做几种选择,在保证无死锁的状态下关掉innodb_deadlock_detect死锁检测。或者用中间件控制并发量

数据结构与算法-数组

数组是一种线性表结构,用连续的内存来储存一组相同类型的数据,在C语言自学数组和指针中有做过这方面的一些笔记

线性结构

线性表包括 数组 队列 链表,他们的特点就是有前后两个方向并且数据排成一条线

线性结构

连续内存

连续的内存相同的数据类型是数组的另一个特性,它构造了一个高效的特性随机访问

顺序访问:类似于链表要找到下标为3的元素,需要1->2->3一级一级往下找 O(n)
随机访问:获取起始内存地址,偏移3找到对应的值 O(1)

1
a[i]_address = base_address + i * data_type_size

性能劣势

虽然数组有随机访问访问的特性,访问时间复杂度只有O(1),但是在删除或者插入的时候有最好O(1)最差O(n)平均O(n)的时间复杂度是不高效的。

插入优化:数组若无序插入新的元素时,可以把这个元素放到数组尾部,然后再这个元素的位置上插入新元素O(1)

删除优化:合并多次删除操作,只是单纯标记删除。在内存不够或者特点情况下触发物理删除,可以减少后面元素移动次数

越界

1
2
3
4
5
6
7
8
9
10
int main(int argc, char* argv[]){
int i = 0;
int arr[3] = {0};
for(; i<=3; i++){
arr[i] = 0;
printf("hello world\n");
}
return 0;
}

上面这段代码,arr的长度是3,i<=3。说明for循环体内会进行4次操作,但是arr的最大容量为3超出了我们划分的数组容量。C语言中的变量会放在内存栈中顺序是 i,a[2],a[1],a[0]。在a[3]这次寻址中会找到对应i的内存地址赋值为0会造成死循环。(数组越界后的执行结果未决,和编程语言和编译器都有关系)

为什么数组下标为0

  1. 最早的C语言是这样,后续语言为了延续习惯
  2. 寻址是 a[n] = 起始地址+n*偏移大小 如果小标1开始就是 a[n] = 起始地址+(n-1)*偏移大小 这样会多一次计算量

溜了,睡觉。。。。

请我喝杯咖啡吧~