浮点数的使用
浮点数的表示方法
浮点数常采用符号+阶码+尾码的表示方法,单精度浮点数由1位符号位,8位阶码和23位尾码表示;双精度浮点数由1位符号位,11位阶码和52位尾码表示。
12float f = 3.14;cout << bitset<32>(*reinterpret_cast<int*>(&f)) << endl; // 01000000 01001000 11110101 11000011
浮点数并不能完整地表述所有的十进制小数,就好像十进制小数不能完整表示三分之一一样,浮点数就不能在没有精度丢失的情况下表示0.1。
123float zero_one = 0.1;cout.precision(9);cout << fixed << zero_one << endl; // 0.100000001
当浮点数多次累加时,会导致误差也累加:
12345float sum = 0;for(int i = 0; i < 10000; i++) { sum += 0.1; ...
容器如何使用分配器
std::allocator
std::allocator是标准库提供的默认分配器模板,如果容器中没有显式指明分配器的话,就会使用这个分配器模板的实例为容器申请和释放内存。
std::allocator的使用方式也很简单:
123456789allocator<int> alloc;int *numptr = alloc.allocate(2);cout << *numptr << endl;cout << *(numptr + 1) << endl;*numptr = 42;*(numptr + 1) = 13;cout << *numptr << endl;cout << *(numptr + 1) << endl;alloc.deallocate(numptr, 2);
这里实例化了一个int类型的分配器alloc,该实例对象只有两个成员函数allocate和deallocate,分别对应申请内存和释放内存(在C++17之前,std::allocator还有两个成员函数co ...
clang-format代码格式化
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220 ...
析构顺序问题
问题描述
在测试有缓存的日志写入时,在Logger对象的析构函数中写了析构时的处理逻辑,但程序退出后未写满的Carrier总是无法写到文件中。经过调试发现,程序结束时Sinker先于前端Logger对象析构,导致在将日志转发给Sinker时抛出异常。
问题分析
经过排查调试和求助ChatGPT,问题在于对象的析构顺序,在初始的版本中,Logger对象是调用了SinkSplitter这个静态单例对象来绑定后端接收器Sinker和传递日志消息的,问题就出在SinkSplitter这个静态对象的实例化上,分流器对象的实例化是通过instance方法来延迟实例化的,这就导致当第一次调用了绑定接收器的函数时,分流器才会被实例化。C++对于静态对象的析构顺序是按照栈的方式先构造的对象后析构,这就导致了分流器的生命周期短于Logger对象,于是当Logger对象进入析构函数,并开始处理未来得及写入的日志时,分流器中保存的后端接收器已经跟随着分流器的析构而析构了。
归根究底,是没有处理好对象间的依赖关系,分流器的生命周期需要大于等于Logger对象,这样才能保证在Logger对象析构时,未来得及处理的 ...
wtlog日志库设计
wtlog日志库整体设计
wtlog日志库由前后端两部分组成,前端Logger是日志信息的入口,负责将原始的日志信息格式化成相应的日志格式,然后传递给日志接收端Sinker,后端的日志接收端负责将接收到的日志按照预定的方式写入到目标位置,比如磁盘文件或是终端等。
日志的传递通过Carrier类进行封装,一条日志在进入Logger后,直到写入目标位置前都会封装在某个Carrier中。
Logger
Logger应该由一个日志工厂统一进行创建和管理,避免直接向外暴露创建接口,在外部提供接口函数来屏蔽和简化创建过程。
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859class Logger {public: Logger() = default; virtual ~Logger();public: void attachSinker(std::shared_ptr<sinks::Sinker ...
PostgreSQL优化查询
优化器行为
通过一个例子展示优化器是如何工作的。现在有三张表:
123CREATE TABLE t1(aid int, ...); --1亿行CREATE TABLE t2(bid int, ...); --2亿行CREATE TABLE t3(cid int, ...); --3亿行
每张表对id字段建立了索引,并且有一张关于表1和表2的视图:
12345678CREATE INDEX idx_a ON t1(aid);CREATE INDEX idx_b ON t2(bid);CREATE INDEX idx_c ON t3(cid);CREATE VIEW v ASSELECT * FROM t1, t2WHERE aid = bid;
最后,我们希望执行下面的查询,来查看优化器会做些什么。
1SELECT * FROM v, t3 WHERE v.aid = t3.cid AND cid = 4;
优化分析
先来看看如果优化器是埋头硬干的话,可能会采用什么方法呢?
嵌套循环:这是最容易想到的方法,直接对两张表进行遍历,依次对表中的元组进行比较,总可以获得正确的结果,但 ...
PostgreSQL系统统计信息和日志设置
pg_stat_activity
pg_stat_activity系统表记录了当前数据库活跃的连接信息,可以使用户清楚当前数据库系统正在做什么,包括使用的数据库和当前哪些用户是在连接和使用中。
例如通过查看pid,query_start,state_change,state,query几列,可以获取到当前哪些进程在执行何种查询,以及查询开始的时间和当前的状态。这样在看到一条糟糕的命令时,可以及时地中断掉它的执行。pg提供了pg_cancel_backend和pg_terminal_backend两个函数来执行中断查询的操作,正如函数名字面意思那样,pg_cancel_backend会取消查询操作,但不会断开用户的连接;而pg_terminal_backend更加暴力,直接将查询和数据库的连接全部清除。
其他pg_stat_activity中记录的数据如下所示。
123456789101112131415161718192021222324252627ubuntu=> \d pg_stat_activity View "pg_ca ...
PostgreSQL的多版本并发控制
PostgreSQL表中的系统字段
pgsql在创建表时,除了用户定义的字段外,还会有一些由系统自动创建的隐藏字段,这些隐藏字段无法通过“\d”命令展示出来。
因为表中已经有了这些隐含的字段,用户在创建表时,必须避免自定义的字段名称和这些隐含字段重复,这与字段名是否是关键字没有关系,即便使用双引号括起来也不行。这些系统字段包括如下:
oid:行对象标识符,每行数据都会创建一个唯一的标识id(由于会影响性能,基本已经被弃用了);
tableoid:包含本行的表oid,在对父表进行查询时,使用该字段可以知道某一行是来自父表还是哪个子表的;
xmin:插入此行版本的事务id;
xmax:删除此行时的事务id,如果查询出来的此字段不为0,说明可能删除这行的事务未提交或者回滚了;
cmin:事务内部的插入类操作的命令id,从0开始递增的值;
cmax:事务内部删除类操作的命令id,不是删除命令时,该字段值为0;
ctid:一个行版本在它所处的表内的物理位置。
xmin,xmax,cmin,cmax
这四个字段用于控制数据在多版本中是否对用户可见。
xmin和xmax控制数据在事务间的可见性
...
PostgrSQL执行计划
explain命令
在sql语句前加上explain关键字来显式SQL命令的执行计划。
pgsql中的explain语法格式如下:
1EXPLAIN [(option[, ...])] statement;
其中option有以下几个可选项:
ANALYZE [boolean]
VERBOSE [boolean]
COSTS [boolean]
BUFFERS [boolean]
FORMAT [TEXT|XML|JSON|YAML]
ANALYZE关键字会通过实际执行SQL语句来获得实际执行计划,因此可以获取到执行计划的每一步耗费了多少时间,以及它实际返回的行数;
VERBOSE关键字会显示额外的附加信息,比如计划树中的每个节点输出的各个列,如果有触发器被触发,还会输出触发器的名称,默认值是false;
COSTS关键字会显示每个计划节点的启动成本和总成本,及估计的行数和每行的宽度,默认是true;
BUFFERS关键字会显示缓冲区的使用情况,BUFFERS只能和ANALYZE关键字一起使用,显示的缓冲区信息包括共享块的读写块数,本地块的读写块数,以及临时块读写块数。共享块、本 ...
使用git多人协同开发
流程
使用git进行多人协同开发,依赖于git的创建分支功能,比如一个仓库下,main分支是主分支,然后需要在此基础上开发新的功能,就从main切出去一个新额分支,开发完成后,再rebase或merge回main分支。
每个开发者只需要关心以下几步:
git pull拉取仓库;
git checkout branch 切换到相应的开发分支,需要创建好本地分支和远程分支并关联;
在分支上完成开发;
合并到主分支上。
操作
首先我们建立一个仓库——在托管平台上还要注册,有点麻烦,这里就用在远程服务器上创建一个裸仓库代替:
1git init --bare git_demo.git
假设有两个开发人员一起工作,他们都克隆了这个仓库:
1git clone sshname@ip:path/reponame.git
如果是从托管平台上git下来的话,还需要管理员将开发者加入到仓库中。对应的开发人员本地要配置git的用户名和邮箱
12git config --global user.name 'xxx'git config --global user.email ' ...