cmake超详细入门教程,学不会你捶我~
Posted 攻城狮星河
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cmake超详细入门教程,学不会你捶我~相关的知识,希望对你有一定的参考价值。
文章目录
- 前言
- 一、cmake是什么?
- 二、动手写一个简单的cmake
- 三、CMakeLixts.txt文件基本语法
- 四、如何将编译文件与源文件分离
- 五、使用 cmake 编译多个源文件
- 六、使用 cmake 生成库文件
- 总结
前言
我们在平时编译代码的时,面对简单的代码,直接GCC编译就好,稍微多一点,可以编写Makefile来编译,以上编译起来都很轻松,可是平时工作的时候基本都是大工程呀,代码文件少则几十个文件了,这种情况下就头疼了,咋办呢?今天我来教你使用cmake轻松解决问题~
提示:以下是本篇文章正文内容,下面案例可供参考
一、cmake是什么?
Makefile想必大家都不陌生吧,它能够解决我们的自动化编译问题,大多是IDE软件都集成了make,譬如 Visual C++的 nmake、linux 下的 GNU make、Qt 的 qmake 等等。
不同的IDE所集成的make工具所遵循的规范和标准都不同,也就导致其语法、格式不同,也就不能很好的跨平台编译,会再次使得工作繁琐起来
那么cmake为了解决这个问题而诞生了,其允许开发者指定整个工程的编译流程,在根据编译平台,生成本地化的Makefile和工程文件,最后用户只需make编译即可
简而言之,可以把cmake看成一款自动生成 Makefile的工具,所以编译流程就变成了:cmake—>make–>用户代码–>可执行文件
二、动手写一个简单的cmake
1.安装cmake
首先需要手动安装cmake
sudo apt-get install cmake
2.cmake编译单个源文件
我们首先编写一个经典的 “hello world” 代码
然后编写 CMakeLists.txt 文件,这个文件会被 cmake 工具解析,可以类比与 Makefile 被 make 解析一样
写下以下内容:
此时,当前文件夹下有连个文件:
执行cmake 进行编译
cmake ./
打印结果如下:
然后会生成这些文件
到这里,我们直接执行make编译代码就好
然后你会发现,已经生成了可执行文件 hello
运行可执行文件成功,也意味着我们已经学会了如何基础的使用 cmake 工具了
三、CMakeLixts.txt文件基本语法
刚刚我们已经自己动手写了一个CMakeLists.txt 文件,并已经成功的编译出了可执行文件,但是你肯定还是处于一种似懂非懂的状态,所以,在这里我们稍微讲下他的语法结构
回到刚刚写的 CMakeLists.txt 文件,我们了解一下他的意思
第一行 project(HELLO):
project 命令用于设置工程的名称,括号中的参数 HELLO 便是我们要设置的工程名称;设置工程名称并
不是强制性的,但是最好加上。
第二行 add_executable(hello ./main.c):
add_executable 同样也是一个命令,用于生成一个可执行文件,
在本例中传入了两个参数,第一个参数表示生成的可执行文件对应的文件名,第二个参数表示对应的源文件;
所以 add_executable(hello ./main.c)表示需要生成一个名为 hello 的可执行文件,所需源文件为当前目录下的 main.c。
四、如何将编译文件与源文件分离
之前我们编译完代码后,会发现编译构建过程产生的文件与我们的源文件混杂在一起了
这样看起来会很不舒服,因此我们可以使用以下方法进行分离
首先,我们把之前构造过程产生的文件都删了,变成这样
新建一个 build 目录,进入目录,执行 cmake …/ , 然后执行 make 编译
可以看到,cmake 和 make 生成的构建文件,以及可执行文件,都保存在了build目录下
如果要清理工程,直接删除 build 目录即可,这样看起来使得工程文件更加整洁
五、使用 cmake 编译多个源文件
在之前的基础上,我们新建两个文件,hello.c 、hello.h
hello.c
hello.h
因为添加了新的源文件,因此还需要修改 CMakeLists.txt 文件
我们定义了一个 SRC_LIST 变量,SRC_LIST 变量是一个源文件列表,记录生
成可执行文件 hello 所需的源文件 main.c 和 hello.c
在 add_executable 命令引用了该变量
当然也可以不去定义这个变量而直接写入源文件,这样做是为了代码整洁,以后文件多了可以这样操作
现在的目录结构是这样:
接下来的环节和之前一样,进入到build目录下,cmake ,最后make 编译出可执行文件
六、使用 cmake 生成库文件
在我们平时的开发过程中,也有很多场景需要将我们的源码编译成库文件来提供使用,这个需求也可以使用cmake做到,
这就需要用到一条命令:
add_library(libhello 静态/动态库 hello.c)
没设置参数的话,默认就是生成静态库文件,可以加参数,设置指定的库文件
add_library(libhello SHARED hello.c) #生成动态库文件
add_library(libhello STATIC hello.c) #生成静态库文件
修改生成的库文件名字
可以看到刚刚的库文件名: liblibhello.a ,非常不优雅
那么我们可以使用这条命令来进行库文件的设置
set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")
set_target_properties 用于设置目标 的属性,这里通 过 set_target_properties 命令 对 libhello 目标 的
OUTPUT_NAME 属性进行了设置,将其设置为 hello。
同样的操作,进入build目录 ,进行以下操作,我们就得到了hello的动态库:libhello.so
总结
关于cmake的使用方法还有很多,这里只是做了一个最基本的介绍,后期有机会在进行详细补充~
分库分表方案深入讲解,学不会你捶我
一、背景介绍
移动互联网时代,随着软件用户量的不断增长,由此产生的数据量也在飞速增长,比如,用户表、订单表、聊天消息表等。据统计,MySQL单表
可以存储10亿级
数据,只是这时候性能比较差,业界公认MySQL
单表容量在1KW量级
是最佳状态,因为这时它的BTREE
索引树高在3~5之间
。
既然一张表无法搞定,那么就想办法将数据存放到多个地方,目前比较普遍的方案有3个:
分区
要求数据不是海量(分区数有限,存储能力就有限)
业务并发能力要求不高
分库分表
互联网行业处理海量数据的通用方法
发展几十年的
RDBMS
(关系型数据库)具有生态完善、绝对稳定、事务特性的优点,只要有软件的地方,它都是核心存储的首选NoSQL/NewSQL
NoSQL/NewSQL
宣传的无论多厉害,就现在各大公司对它的定位,都是RDBMS
的补充,而不是取而代之
本文就分库分表
的一些核心流程展开介绍:
二、是单库分表,还是分库分表
单库分表适用场景:
单表数据量太大,查询时需要扫描的行数太多,
SQL
执行效率低下CPU
出现瓶颈分库分表适用场景:
磁盘读
IO
达到了瓶颈:热点数据太多,数据库缓存放不下,每次查询时会产生大量的IO
,导致查询速度低下请求的数据太多、网络带宽不够
数据库连接数超过了最大限定值
分库分表
的复杂度要高于单库分表
,如果数据量不是特别大,且QPS
也不是特别高,首选单库分表
,待某些指标有接近阈值的迹象时,再考虑分库分表。
分库分表
相对单库分表
来说,复杂的地方有:
需要约定多个分片数据源
需要定义多个分片数据源的事务管理器
数据一致性的处理方案(强一致 or 最终一致)
数据迁移、后续扩容
...
三、分布式数据库中间件选型
本文主要针对时下比较流行的两款数据库中间件产品做下介绍:
主要指标 | Sharding-JDBC | MyCat |
---|---|---|
所属 | Apache | 基于阿里 Cobar 二次开发,社区维护 |
活跃度 | 高 | 高 |
ORM支持 | 任意 | 任意 |
基于客户端还是服务端 | 客户端 | 服务端 |
分库 | 支持 | 支持 |
分表 | 支持 | 不支持单库分表 |
事务 | 自带弱XA、最大努力送达型柔性事务 | 自带弱XA |
监控 | 无,可通过其它方式支持 | 自带 |
读写分离 | 支持 | 支持 |
限制 | 部分 JDBC 方法不支持、SQL语句限制 | 部分 JDBC 方法不支持、SQL语句限制 |
数据库连接池 | 任意 | 任意 |
MySQL交互协议 | JDBC Driver | 前后端均用 NIO |
开发 | 开发成本高,代码入侵大 | 开发成本小,代码入侵小 |
运维 | 维护成本低 | 维护成本高 |
配置难度 | 一般 | 复杂 |
Sharding-JDBC
架构图:
简单介绍:
Sharding-JDBC
是一款轻量级的框架,以工程依赖JAR
的形式提供功能,无需额外部署和依赖,可以理解为增强版的JDBC
驱动对于运维同事来说,只需要协助一些简单的配置及后续的扩容工作,无需关注底层代码与分片策略规则,相对
MyCat
,这是Sharding-JDBC
的优势,减少了部署成本以及运维同事的学习成本
MyCat
架构图
简单介绍:
MyCat
并不是业务系统代码里面的配置,而是独立运行的中间件,所有配置都会交给运维同事执行对于运维同事来说,它是在数据库
Server
前增加的一层代理,MyCat
本身不存数据,数据存储在后端的数据库上,因此,数据可靠性以及事务等都是通过数据库保证的MyCat
down 掉的时候,系统不能对数据库进行操作,会对所有用户产生影响MyCat
比较适合大数据工作
通过以上分析,可见
Sharding-JDBC
相对MyCat
来说,更轻量,首选肯定是Sharding-JDBC
,只要代码层面做好防腐层(依赖倒置)的设计,就算以后数据量级达到了百亿、千亿,也可以更加灵活方便的替换其它中间件产品,甚至NewSQL
。
四、分布式ID的生成方式
实现方式
完全依赖数据源的方式:
ID
的生成规则,读取控制完全由数据源控制,常见的如数据库自增长ID
、优雅的Flickr
方案、基于Redis
的原子操作incr/incrBy
产生顺序号、Mongodb
的ObjectId
、美团(Leaf
)的号段模式...半依赖数据源的方式:
ID
的生成规则,有部分生成因子需要由数据源(或配置信息)控制,如百度的uid-generator
、美团(Leaf
)的Snowflake
模式...不依赖数据源的方式:
ID
的生成规则完全由机器信息独立计算,不依赖任何配置信息和数据记录,如常见的UUID
及变种、GUID
...
实践方案
实践方案适用于以上提及的三种实现方式,可作为这三种实现方式的一种补充,旨在提升系统吞吐量,但原有实现方式的局限性依然存在。
实时获取方案:顾名思义,每次要获取
ID
时,实时生成,简单快捷,ID
都是连续不间断的,但吞吐量可能不是最高的。预生成方案:预先生成一批
ID
放在数据池里,可简单自增长生成,也可以设置步长,分批生成,需要将这些预先生成的数据,放在存储容器里(JVM
内存,Redis
,数据库表均可以),可以较大幅度地提升吞吐量,但需要开辟临时的存储空间,断电宕机后可能会丢失已有ID
,ID
也可能出现间断。
选择分布式 ID 的生成方式时,需要特别注意以下几个地方:
全局唯一:必须保证
ID
是全局唯一的高可用:无限接近于
100%
的可用性高性能:低延时,
ID
生成响应要快接入方便:遵循拿来主义原则,在系统设计和实现上要尽可能的简单
长度适中:不要太长,最好
64bit
,使用long
比较好操作。如果是96bit
,需要各种移位,相当的不方便,还有可能有些组件不能支持这么大的ID
分片支持:可以控制
ShardingId
,比如某一个用户的文章要放在同一个分片内,这样查询效率高,修改也容易,实现稍复杂信息安全:如果
ID
是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL
即可;如果是订单号就更危险了,竞争对手可以直接知道我们一天的订单量,所以要结合自己的业务场景来考虑
如果系统要求的吞吐量不是极高,个人推荐了解下优雅的
Flickr
方案,如果系统要求的吞吐量极高,个人推荐了解下美团的(Leaf
)项目,由于篇幅有限,这里不做过多展开。
五、分片/表键选择
分片键的定义
分片键即分库分表的拆分字段,是在水平拆分过程中用于生成拆分规则的数据表字段,根据分片键的值将数据表水平拆分到每个分库/分表中。
数据表拆分的首要原则
要尽可能的找到数据表中的数据在业务逻辑上的主体,并确定大部分(或核心的)数据库操作(查、删、改)都是围绕这个主体进行的,那么就可以使用该主体对应的字段作为分片键。
业务逻辑上的主体,通常与业务的应用场景相关,下面的一些典型应用场景都有明确的业务逻辑主体:
面向用户的互联网应用,都是围绕用户维度来做各种操作,那么业务逻辑主体就是用户,可使用用户对应的字段作为分片键
侧重于卖家的电商应用,都是围绕卖家维度来进行各种操作,那么业务逻辑主体就是卖家,可使用卖家对应的字段作为分片键
如果实在找不到合适的业务逻辑主体作为分片键,可以考虑使用下面的方式来选择分片键:
根据数据分布和访问的均衡度来考虑分片键,尽量将拆分后的每张分表中的数据相对均匀地分布在不同的物理分表中,这比较适用于大量分析型查询的应用场景
按照数字(字符串)类型与时间类型字段相结合作为分片键,这比较适用于日志检索类的应用场景
确定了业务逻辑的主体,但是还有其它的附属主体
该怎么办?
比如消息表(message
)有以下几个核心字段:
消息表中,两个聊天对象可以唯一的确定一个会话,即一个会话下的消息是一个独立的集合,大部分操作(查、删、改)都是围绕这个集合展开的,故可以拿session_id
当作业务逻辑的主体,但是,通过message_id
和guid
操作的业务场景也比较多,此时可以考虑通过冗余映射表来辅助操作:
即message_message_id_mapping_*
和message_guid_mapping_*
分别是message_*
的冗余映射表(ps,以_*
结尾的表,表示分表,如message_*
表示消息分表;橙色标记的字段代表表的分片键):
当根据
session_id
查询时,根据指定的分片规则
(下文会介绍),可以直接查询message_*
表当根据
message_id
查询时,根据指定的分片规则,先查询message_message_id_mapping_*
,拿到session_id
后,再根据session_id
(根据指定的分片规则)和message_id
查询message_*
表当根据
guid
查询时,根据指定的分片规则,先查询message_guid_mapping_*
,拿到session_id
后,再根据session_id
(根据指定的分片规则)和guid
查询message_*
表
如果对查询的实时性要求很高,可以冗余全量数据,即
冗余全量表 VS 冗余映射表
速度对比:冗余全量表速度更快,冗余映射表需要二次查询,即使有引入缓存,还是多一次网络开销
存储成本:冗余全量表需要几倍于冗余映射表的存储成本
维护代价:冗余全量表维护代价更大,涉及到数据变更时,多张表都要进行修改
或许有人注意到,冗余映射表的后缀也加了
_*
,没错,冗余映射表也要进行分库分表。或许有人又说冗余映射表也要分库分表,复杂度比较高,可以将冗余映射表的数据全部存到
ES
,这也是可以的,网上确实有这样的案例,不过在开始之前,要结合系统的业务定位及使用额外组件的复杂度做一份调研,最终选定最合适的方案。注意: 如果决定将冗余映射分表存储到数据库中,要注意给冗余映射分表的分片键和映射字段(如本例中
session_id
)建立组合索引,保证查询时,可以用到覆盖索引,加快查询性能。
六、分片算法/规则
常用的分片算法有取模分片、范围分片、hash
分片、复合分片等,这里不做过多展开,只介绍最常用的两种:取模分片、范围分片。还是拿五、分片/表键选择
中的消息表(采用冗余映射表的方式)为例,为了方便介绍,只介绍单库分表的分片算法,分库分表无非多了个分库规则,基本同分表规则。
其中,session_id
和message_id
都是分布式的自增ID
,guid
是随机生成的GUID
,故可以进行如下的分片规则:
session_id
采用取模分片:两个聊天对象可以唯一确定一个session_id
,为了保证每张分表数据的相对均匀,message_*
采用取模分片
算法message_id
采用范围分片:message_id
是自增的分布式ID
,大部分根据message_id
的操作(查、删、改)集中于最新的一部分数据,为了兼容操作最近消息的场景及方便扩容,message_message_id_mapping_*
采用范围分片(采用范围分片,大部分操作只会落在最新的几张分表中,操作的分表越少,查询性能越高)guid
采用取模分片:guid
没有规律而言,为了保证分表数据的相对均匀,message_guid_mapping_*
采用取模分片算法
通过上面的举例,可以看出,选择分片算法要综合考虑分片键的业务场景、字段值的规律、分片后分表的数据分布、后续的扩容... 注意: 对于取模分片的分表数量,建议是
2
的N
次幂,好处是方便动态缩容...对于程序员来说,2
的N
次幂,总是有那么一些微妙的地方以上是关于cmake超详细入门教程,学不会你捶我~的主要内容,如果未能解决你的问题,请参考以下文章