• [技术干货] 【第四届openGauss征文活动参赛作品】openGauss初学者快速入门
    前言我第一次接触openGauss是再2022年8月份,当时有一个鲲鹏应用大赛,我对于openGauss很陌生,在网上看了很多有关openGauss的视频,我在bilibili上看到松鼠会发布的openGauss讲解是最多的,然后我就对着视频搭建openGauss,当时还好我电脑上一直有centos7的虚拟环境,所以我直接利用centos搭建openGauss,这里给大家分享一下我入门的学习是怎么样的。一、openGauss是什么?官方解释:openGauss是一款全面友好开放,携手伙伴共同打造的企业级开源关系型数据库。openGauss提供面向多核架构的极致性能、全链路的业务、数据安全、基于AI的调优和高效运维的能力。openGauss深度融合华为在数据库领域多年的研发经验,结合企业级场景需求,持续构建竞争力特性。openGauss网站 (https://opengauss.org/zh/ ) 提供了有关openGauss软件的最新信息。个人理解:我认为是一个基于Linux的一款国内数据库,其实和Mysql等数据库一样,但是主要还是觉得这是我们骄傲的民族企业华为开发的,安全方面我会更放心。最主要的原因是openGauss属于开源数据库,对我们开发者来说是很有好的,学习起来也很方便。二、openGauss软件架构下面这张图也是我从官方的资料中找到的:这张图片,我们可以很明显的看到,在openGauss数据库中,是有一个单独的备份机制存在,也是对数据防止丢失做的一种机制吧。其中图片中的名词下面附上官方解释:OM运维管理模块(Operation Manager)。提供数据库日常运维、配置管理的管理接口、工具。个人理解:方便维护的管理员模块CM数据库管理模块(Cluster Manager)。管理和监控数据库系统中各个功能单元和物理资源的运行情况,确保整个系统的稳定运行。个人理解:针对数据库的管理模块客户端驱动客户端驱动(Client Driver)。负责接收来自应用的访问请求,并向应用返回执行结果。客户端驱动负责与openGauss实例通信,发送应用的SQL命令,接收openGauss实例的执行结果。openGauss(主备)openGauss主备(Datanode)。负责存储业务数据、执行数据查询任务以及向客户端返回执行结果。openGauss实例包含主、备两种类型,支持一主多备。建议将主、备openGauss实例分散部署在不同的物理节点中。个人理解:安全机制的考虑Storage服务器的本地存储资源,持久化存储数据。个人理解:就是长久的本地资源三、openGauss特点特点针对特点解释高性能通过列存储、向量化执行引擎、融合引擎等关键技术,实现百亿数据量查询秒级响应高可用同城跨AZ(Available Zone)容灾,数据不丢失,分钟级恢复高安全性支持访问控制、加密认证、数据库审计、动态数据脱敏等安全特性,提供全方位端到端的数据安全保护可维护性好支持WDR诊断、慢SQL诊断、Session诊断等多种维护手段,准确快速定位问题。具备AI4DB能力,能够通过AI算法实现数据库自调优、自监控、自诊断等我个人认为最大的特点就是,高安全性,但是目前还在发展阶段,openGauss的开发人员真的是需要加把劲了,之前我在学习openGauss的时候,就发现网站是存在漏洞的,而且很多,如果不更加细致的去处理好的话,对于数据的安全真的是不敢恭维!!!! 希望官方大大还是更加注意开发质量四、个人学习1.下载我个人选择的是极简版,也是为了方便,在文章开始我也说了,我主要还是搭建在centos7上面​2.安装环境centos7​3.基础配置解压之后,我们按照顺序输入下面的命令,使用VIM打开config文件。 vim /etc/selinux/config 修改“SELINUX”的值“disabled”,这里用的是vim编辑器,如果不懂的话请自学一下 SELINUX=disabled 重启centos。 reboot 检查防火墙状态。 systemctl status firewalld 在没有关闭的情况下,关闭防火墙 systemctl disable firewalld.service systemctl stop firewalld.service4.其他配置设置编码,/etc/profile文件下添加“export LANG=Unicode vim /etc/profile 设置时间 date #查看时间 将/usr/share/zoneinfo/目录下的时区文件复制为/etc/localtime文件下面 cp /usr/share/zoneinfo/配置的时间地点 /etc/localtime基本上因为centos7很多配置是已经默认的,所以相对而言搭建很简单。五、建议1.推广形式可以加强,首先,我了解到推广的形式,主要还是比赛,活动,我认为可以发布会,公众号视频,还可以找博主在一些平台上,撰写一个月,效果可能会更好。2.建议可以出版openGauss的书籍,出版,推广书籍(这个我看到捉虫活动有这样的书籍)3.另外是,我个人感觉对于openGauss的学习视频资料太少了,可以联合直播,扩展了解的人群。4.可以在行业中找到突破口,目前大厂数据库更换的话太昂贵,可以先占领中小型公司的市场。最后,支持华为,支持松鼠会,支持openGauss
  • [技术干货] 【第四届openGauss征文活动参赛作品】奇思妙想——通过Go语言自制安装openGauss二进制程序
    前言巧妙利用go语言自制openGauss安装二进制程序,经测试15s即可安装完成一、安装go语言环境1. 下载解压go[root@node1 ~]# wget https://golang.google.cn/dl/go1.19.1.linux-amd64.tar.gz[root@node1 ~]# tar -zxvf go1.19.1.linux-amd64.tar.gz -C /usr/local2. 添加环境变量export PATH=$PATH:/usr/local/go/bin3. 测试运行package main import fmt func main(){ fmt.Println("hello,world!") }[root@node1 ~]# go run test.go hello,world!二、 openGauss安装 (15s安装完成)1. 源代码[root@node1 ~]# cat gaussdb.go package main import ( "fmt" "io/ioutil" "os/exec" ) func main() { cmd := exec.Command("/bin/bash", "-c", `useradd omm ;echo "Enmo@123" | passwd --stdin omm > /dev/null ;mkdir -p /opt/mogdb/software;chown -R omm:omm /opt/;tar -xf openGauss-3.1.0-CentOS-64bit.tar.bz2 -C /opt/mogdb/software; su - omm -c "echo 'export GAUSSHOME=/opt/mogdb/software' >> /home/omm/.bashrc ;echo 'export PATH=\$GAUSSHOME/bin:\$PATH' >> /home/omm/.bashrc ;echo 'export LD_LIBRARY_PATH=\$GAUSSHOME/lib:\$LD_LIBRARY_PATH' >> /home/omm/.bashrc;source /home/omm/.bashrc;gs_initdb --pgdata=/opt/mogdb/data --nodename=primary --pwpasswd=Enmo@123 --encoding=UTF-8 --locale=en_US.UTF-8 > /dev/null ;echo \"port=26000\" >> /opt/mogdb/data/postgresql.conf;echo \"listen_addresses = '0.0.0.0'\" >> /opt/mogdb/data/postgresql.conf;echo \"password_encryption_type = 0\" >> /opt/mogdb/data/postgresql.conf;echo \"log_directory = 'pg_log'\" >> /opt/mogdb/data/postgresql.conf;echo \"remote_read_mode=non_authentication\" >> /opt/mogdb/data/postgresql.conf;echo \"host all all 0.0.0.0/0 md5\" >> /opt/mogdb/data/pg_hba.conf;gs_ctl start -D /opt/mogdb/data > /dev/null ;gsql -d postgres -p 26000 -c'select version();select pg_postmaster_start_time();';echo -e 'data_user is omm ! \ndata_port is 26000 ! \ndata_path is /opt/mogdb/data ! \ndata_soft is /opt/mogdb/software !'"`) // cmd := exec.Command("/bin/bash", "-c", `df -h;ls`) //创建获取命令输出管道 stdout, err := cmd.StdoutPipe() if err != nil { fmt.Printf("Error:can not obtain stdout pipe for command:%s\n", err) return } //执行命令 if err := cmd.Start(); err != nil { fmt.Println("Error:The command is err,", err) return } //读取所有输出 bytes, err := ioutil.ReadAll(stdout) if err != nil { fmt.Println("ReadAll Stdout:", err.Error()) return } if err := cmd.Wait(); err != nil { fmt.Println("wait:", err.Error()) return } fmt.Printf("stdout:\n\n %s", bytes) }2. go run 测试运行准备安装包[root@node1 ~]# ls openGauss-3.1.0-CentOS-64bit.tar.bz2 gaussdb.go gaussdb.go openGauss-3.1.0-CentOS-64bit.tar.bz2go run[root@node1 ~]# go run gaussdb.go stdout: version ------------------------------------------------------------------------------------------------------------------------------------------------------ (openGauss 3.1.0 build 2c0ccaf9) compiled at 2022-09-25 19:32:58 commit 0 last mr on x86_64-unknown-linux-gnu, compiled by g++ (GCC) 7.3.0, 64-bit (1 row) pg_postmaster_start_time ------------------------------- 2022-09-28 13:38:28.550462+08 (1 row) data_user is omm ! data_port is 26000 ! data_path is /opt/mogdb/data ! data_soft is /opt/mogdb/software !连接测试 ```s [root@node1 ~]# su - omm Last login: Wed Sep 28 13:39:38 CST 2022 [omm@node1 ~]$ gsql -d postgres -p26000 -r gsql ((openGauss 3.1.0 build 2c0ccaf9) compiled at 2022-09-25 19:32:58 commit 0 last mr ) Non-SSL connection (SSL connection is recommended when requiring high-security) Type "help" for help.openGauss=# select version(); version(openGauss 3.1.0 build 2c0ccaf9) compiled at 2022-09-25 19:32:58 commit 0 last mr on x86_64-unknown-linux-gnu, compiled by g++ (GCC) 7.3.0, 64-bit (1 row)openGauss=# \q [omm@node1 ~]$ logout- go build 二进制 ```s [root@node1 ~]# go build gaussdb.go [root@node1 ~]# ls gaussdb openGauss-3.1.0-CentOS-64bit.tar.bz2 gaussdb.go gaussdb gaussdb.go openGauss-3.1.0-CentOS-64bit.tar.bz2清理环境[root@node1 ~]# cat a.sh pkill -9 gaussdb rm -rf /opt/mogdb/* userdel -r omm [root@node1 ~]# sh a.sh安装 ```s [root@node1 ~]# date;./gaussdb;date Wed Sep 28 13:53:12 CST 2022 stdout: version(openGauss 3.1.0 build 2c0ccaf9) compiled at 2022-09-25 19:32:58 commit 0 last mr on x86_64-unknown-linux-gnu, compiled by g++ (GCC) 7.3.0, 64-bit (1 row)pg_postmaster_start_time2022-09-28 13:53:27.034021+08 (1 row)data_user is omm ! data_port is 26000 ! data_path is /opt/mogdb/data ! data_soft is /opt/mogdb/software ! Wed Sep 28 13:53:27 CST 2022- 连接测试 ```s [root@node1 ~]# su - omm Last login: Wed Sep 28 13:53:19 CST 2022 [omm@node1 ~]$ gsql -d postgres -p26000 -r gsql ((openGauss 3.1.0 build 2c0ccaf9) compiled at 2022-09-25 19:32:58 commit 0 last mr ) Non-SSL connection (SSL connection is recommended when requiring high-security) Type "help" for help. openGauss=# select version(); version ------------------------------------------------------------------------------------------------------------------------------------------------------ (openGauss 3.1.0 build 2c0ccaf9) compiled at 2022-09-25 19:32:58 commit 0 last mr on x86_64-unknown-linux-gnu, compiled by g++ (GCC) 7.3.0, 64-bit (1 row) openGauss=# \q
  • [技术干货] 【第四届openGauss征文活动参赛作品】openGauss账本数据库,你不知道的那些事儿
    openGauss账本数据库,你不知道的那些事儿摘要本文将通过对比官方文档关于 “设置账本数据库” 中的几个章节,结合源码来说说文档中操作步骤背后的原理。账本数据库概述你知道的那些事儿官方文档账本数据库融合了区块链思想,将用户操作记录至两种历史表中:用户历史表和全局区块表。当用户创建防篡改用户表时,系统将自动为该表添加一个hash列来保存每行数据的hash摘要信息,同时在blockchain模式下会创建一张用户历史表来记录对应用户表中每条数据的变更行为;而用户对防篡改用户表的一次修改行为将记录至全局区块表中。由于历史表具有只可追加不可修改的特点,因此历史表记录串联起来便形成了用户对防篡改用户表的修改历史。你不知道的那些事儿操作步骤1.创建防篡改模式。openGauss=# CREATE SCHEMA ledgernsp WITH BLOCKCHAIN;首先在这个SQL中我们可以看到WITH BLOCKCHAIN ,这里说明创建出来的SCHEMA与普通的SCHEMA不同,但就行不同在哪里我们后面会提到。从语法解析看,增加了对BLOCKCHAIN的处理,标记了是否为账本模式。 CreateSchema ::= CREATE SCHEMA schema_name [ AUTHORIZATION user_name ] [WITH BLOCKCHAIN] [ schema_element [ ... ] ];CreateSchemaStmt 结构中增加了bool类型字段hasBlockChaintypedef struct CreateSchemaStmt { NodeTag type; char *schemaname; /* the name of the schema to create */ char *authid; /* the owner of the created schema */ bool hasBlockChain; /* whether this schema has blockchain */ List *schemaElts; /* schema components (list of parsenodes) */ TempType temptype; /* if the schema is temp table's schema */ List *uuids; /* the list of uuid(only create sequence or table with serial type need) */ } CreateSchemaStmt;你不知道的限制账本数据库对于ALTER SCHEMA的几个限制1、dbe_perf和snapshot两个模式不能ALTER为blockchain模式。 if (withBlockchain && ((strncmp(nspName, "dbe_perf", STR_SCHEMA_NAME_LENGTH) == 0) || (strncmp(nspName, "snapshot", STR_SNAPSHOT_LENGTH) == 0))) { ereport(ERROR, (errcode(ERRCODE_OPERATE_FAILED), errmsg("The schema '%s' doesn't allow to alter to blockchain schema", nspName))); }2、系统模式不能 ALTER 为blockchain模式。 if (withBlockchain && !g_instance.attr.attr_common.allowSystemTableMods && !u_sess->attr.attr_common.IsInplaceUpgrade && IsReservedName(nspName)) ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("The system schema \"%s\" doesn't allow to alter to blockchain schema", nspName)));3、包含了表的SCHEMA不能ALTER为blockchain模式。 /* * If the any table exists in the schema, do not change to ledger schema. */ StringInfo existTbl = TableExistInSchema(HeapTupleGetOid(tup), TABLE_TYPE_ANY); if (existTbl->len != 0) { if (withBlockchain) { ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("It is not supported to change \"%s\" to blockchain schema which includes tables.", nspName))); } else { ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("It is not supported to change \"%s\" to normal schema which includes tables.", nspName))); } }查看模式2.在防篡改模式下创建防篡改用户表。openGauss=# CREATE TABLE ledgernsp.usertable(id int, name text);你不知道的限制创建账本表的同时会自动创建一个“历史表”和“历史表的索引”。在建表时CreateCommand会调用AlterCreateChainTables,如果是账本表再去调用create_hist_relation来创建历史表CreateCommand -> AlterCreateChainTables -> create_hist_relation/* * AlterCreateChainTables * If it is a ledger usertable, that should invoking this function. * then create a history table. */ void AlterCreateChainTables(Oid relOid, Datum reloptions, CreateStmt *mainTblStmt) { Relation rel = NULL; rel = heap_open(relOid, AccessExclusiveLock); /* Ledger user table only support for the regular relation. */ if (!rel->rd_isblockchain) { heap_close(rel, NoLock); return; } create_hist_relation(rel, reloptions, mainTblStmt); heap_close(rel, NoLock); }历史表命名规则,参见函数get_hist_namebool get_hist_name(Oid relid, const char *rel_name, char *hist_name, Oid nsp_oid, const char *nsp_name) { errno_t rc; if (!OidIsValid(relid) || rel_name == NULL) { return false; } nsp_oid = OidIsValid(nsp_oid) ? nsp_oid : get_rel_namespace(relid); nsp_name = (nsp_name == NULL) ? get_namespace_name(nsp_oid) : nsp_name; int part_hist_name_len = strlen(rel_name) + strlen(nsp_name) + 1; if (part_hist_name_len + strlen("_hist") >= NAMEDATALEN) { rc = snprintf_s(hist_name, NAMEDATALEN, NAMEDATALEN - 1, "%d_%d_hist", nsp_oid, relid); securec_check_ss(rc, "", ""); } else { rc = snprintf_s(hist_name, NAMEDATALEN, NAMEDATALEN - 1, "%s_%s_hist", nsp_name, rel_name); securec_check_ss(rc, "", ""); } return true; }表名最大长度 #define NAMEDATALEN 64如果没有超过长度限制:schema_table_hist如果超过长度限制:schema(oid)_talbe(oid)_hist,因为oid是unsigned int 类型最大值为4294967295为10位,所以这种命名规则的最大长度为10+1+10+1+4+\0=27,因此永远不会超过最大长度64。omm=# create schema aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa with blockchain; CREATE SCHEMA omm=# create table aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(id int); CREATE TABLE历史表索引命名规则,参见函数get_hist_name /* now create index for this new history table */ char hist_index_name[NAMEDATALEN]; rc = snprintf_s(hist_index_name, NAMEDATALEN, NAMEDATALEN - 1, "gs_hist_%u_index", relid);命名规则:gs_hist_$(账本表oid)_index。3、修改防篡改用户表数据对防篡改用户表执行INSERT/UPDATE/DELETE。openGauss=# INSERT INTO ledgernsp.usertable VALUES(1, 'alex'), (2, 'bob'), (3, 'peter'); INSERT 0 3 openGauss=# SELECT *, hash FROM ledgernsp.usertable ORDER BY id; id | name | hash ----+-------+------------------ 1 | alex | 1f2e543c580cb8c5 2 | bob | 8fcd74a8a6a4b484 3 | peter | f51b4b1b12d0354b (3 rows) openGauss=# UPDATE ledgernsp.usertable SET name = 'bob2' WHERE id = 2; UPDATE 1 openGauss=# SELECT *, hash FROM ledgernsp.usertable ORDER BY id; id | name | hash ----+-------+------------------ 1 | alex | 1f2e543c580cb8c5 2 | bob2 | 437761affbb7c605 3 | peter | f51b4b1b12d0354b (3 rows) openGauss=# DELETE FROM ledgernsp.usertable WHERE id = 3; DELETE 1 openGauss=# SELECT *, hash FROM ledgernsp.usertable ORDER BY id; id | name | hash ----+------+------------------ 1 | alex | 1f2e543c580cb8c5 2 | bob2 | 437761affbb7c605 (2 rows)查看账本历史操作记录你知道的那些事儿官方文档前提条件系统中需要有审计管理员或者具有审计管理员权限的角色。数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。你不知道的那些事儿基本操作1、查询全局区块表记录。omm=# SELECT * FROM gs_global_chain; blocknum | dbname | username | starttime | relid | relnsp | relname | relhash | globalhash | txcommand ----------+--------+----------+-------------------------------+-------+-----------+-----------+------------------+----------------------------------+---------------- -------------------------------------------------------------- 1 | omm | omm | 2022-09-17 13:59:37.84824+00 | 16404 | ledgernsp | usertable | a41714001181a294 | 83927d11ba1fd678e8f4b0723a9cd5f2 | INSERT INTO led gernsp.usertable VALUES(1, 'alex'), (2, 'bob'), (3, 'peter'); 2 | omm | omm | 2022-09-17 13:59:51.723068+00 | 16404 | ledgernsp | usertable | b3a9ed0755131181 | b5ee73b6c20c817230182f6373c78e20 | UPDATE ledgerns p.usertable SET name = 'bob2' WHERE id = 2; 3 | omm | omm | 2022-09-17 13:59:58.159596+00 | 16404 | ledgernsp | usertable | 0ae4b4e4ed2fcab5 | 0cc9938cf7f1ed7f7f1a03c29954380a | DELETE FROM led gernsp.usertable WHERE id = 3; (3 rows)注册钩子,在对账本做修改操作的时候注册的钩子函数ledger_ExecutorEnd被回调。/* * ledger_hook_init -- install of gchain block record hook. */ void ledger_hook_init(void) { t_thrd.security_ledger_cxt.prev_ExecutorEnd = (void *)ExecutorEnd_hook; ExecutorEnd_hook = ledger_ExecutorEnd; }生成globalhash规则全局区块表记录主要是生成globalhash.调用过程:ledger_ExecutorEnd --> ledger_gchain_append --> set_gchain_comb_string                                                                          --> get_next_g_blocknum                                                                          --> gen_global_hashset_gchain_comb_string,是一组字符串拼接成的:rel_name + nsp_name + query_string + rel_hashget_next_g_blocknum,用全局变量g_blocknum保存gen_global_hash,是的set_gchain_comb_string拼出来的串+上一条的hash值拼串然后再去hash——区块链的基本原理bool gen_global_hash(hash32_t *hash_buffer, const char *info_string, bool exist, const hash32_t *prev_hash) { errno_t rc = EOK; int comb_strlen; char *comb_string = NULL; /* * Previous block not exists means current insertion block is genesis, * then we use global systable as origin combine string for globalhash * generation. If previous block exists, we will use previous global * hash as combine string to calculate globalhash. */ if (!exist) { /* generate genesis block globalhash */ comb_strlen = strlen(GCHAIN_NAME) + strlen(info_string) + 1; comb_string = (char *)palloc0(comb_strlen); rc = snprintf_s(comb_string, comb_strlen, comb_strlen - 1, "%s%s", GCHAIN_NAME, info_string); securec_check_ss(rc, "", ""); } else { /* use previous globalhash and current block info to calculate globalhash. */ char *pre_hash_str = DatumGetCString(DirectFunctionCall1(hash32out, HASH32GetDatum(prev_hash))); comb_strlen = strlen(pre_hash_str) + strlen(info_string) + 1; comb_string = (char *)palloc0(comb_strlen); rc = snprintf_s(comb_string, comb_strlen, comb_strlen - 1, "%s%s", info_string, pre_hash_str); securec_check_ss(rc, "", ""); pfree_ext(pre_hash_str); } if (!pg_md5_binary(comb_string, comb_strlen - 1, hash_buffer->data)) { pfree(comb_string); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Failed to generate globalhash, out of memory"))); return false; } pfree(comb_string); return true; }在src/gausskernel/runtime/executor/nodeModifyTable.cpp中更新_hist表的hash值。通过set_user_tuple_hash得到账本表hash列的值。/* * set_user_tuple_hash -- calculate and fill the hash attribute of user table's tuple. * * tup: row data of user table * rel: user table * hash_exists: whether tuple comes with tuplehash. * * Note: if hash_exists is true, we should recompute * tuple hash and compare with tuplehash of itself. */ HeapTuple set_user_tuple_hash(HeapTuple tup, Relation rel, bool hash_exists) { uint64 row_hash = gen_user_tuple_hash(rel, tup); int hash_attrno = user_hash_attrno(rel->rd_att); if (hash_exists) { bool is_null; Datum hash = heap_getattr(tup, hash_attrno + 1, rel->rd_att, &is_null); if (is_null || row_hash != DatumGetUInt64(hash)) { ereport(ERROR, (errcode(ERRCODE_OPERATE_INVALID_PARAM), errmsg("Invalid tuple hash."))); } return tup; } Datum *values = NULL; bool *nulls = NULL; bool *replaces = NULL; /* Build modified tuple */ int2 nattrs = RelationGetNumberOfAttributes(rel); values = (Datum*)palloc0(nattrs * sizeof(Datum)); nulls = (bool*)palloc0(nattrs * sizeof(bool)); replaces = (bool*)palloc0(nattrs * sizeof(bool)); values[hash_attrno] = UInt64GetDatum(row_hash); replaces[hash_attrno] = true; HeapTuple newtup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls, replaces); pfree_ext(values); pfree_ext(nulls); pfree_ext(replaces); return newtup; }校验账本数据一致性你知道的那些事儿官方文档数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。你不知道的那些事儿基本操作1、校验防篡改用户表ledgernsp.usertable与其对应的历史表是否一致。omm=# SELECT pg_catalog.ledger_hist_check('ledgernsp', 'usertable'); ledger_hist_check ------------------- t (1 row)校验用户权限 Only super user or audit admin have access right to blockchain nsp /* Only super user or audit admin have access right to blockchain nsp */ if (nsp_oid == PG_BLOCKCHAIN_NAMESPACE) { return gs_blockchain_aclmask(roleid, mask); }校验历史表hash值is_hist_hash_identity --> get_usertable_hash_sum                                --> get_histtable_hash_sum/* * is_hist_hash_identity -- check whether user table hash and history table hash are equal * * relid: user table oid * res_hash: hash sum of history table */ bool is_hist_hash_identity(Oid relid, uint64 *res_hash) { uint64 user_hash_sum; uint64 hist_hash_sum; char hist_name[NAMEDATALEN]; char *rel_name = get_rel_name(relid); if (!get_hist_name(relid, rel_name, hist_name)) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("get hist table name failed."))); } Oid histoid = get_relname_relid(hist_name, PG_BLOCKCHAIN_NAMESPACE); if (!OidIsValid(histoid)) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find hist table of \"%s\".", rel_name))); } user_hash_sum = get_usertable_hash_sum(relid); hist_hash_sum = get_histtable_hash_sum(histoid); *res_hash = hist_hash_sum; return user_hash_sum == hist_hash_sum; }2、查询防篡改用户表ledgernsp.usertable与其对应的历史表以及全局区块表中关于该表的记录是否一致。omm=# SELECT pg_catalog.ledger_gchain_check('ledgernsp', 'usertable'); ledger_gchain_check --------------------- t (1 row)校验是否为账本表ledger_usertable_check校验用户权限has_ledger_consistent_privilege校验历史表hash值is_hist_hash_identity计算/校验全局表hash get_gchain_relhash_sum/* * get_gchain_relhash_sum -- calculate relhash from gs_global_chain * * relid: user table oid */ static uint64 get_gchain_relhash_sum(Oid relid) { uint64 relhash = 0; HeapTuple tuple = NULL; /* scan the gs_global_chain catalog by relid */ Relation gchain_rel = heap_open(GsGlobalChainRelationId, AccessShareLock); Form_gs_global_chain rdata = NULL; TableScanDesc scan = heap_beginscan(gchain_rel, SnapshotNow, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { rdata = (Form_gs_global_chain)GETSTRUCT(tuple); if (rdata == NULL || rdata->relid != relid) { continue; } relhash += rdata->relhash; } heap_endscan(scan); heap_close(gchain_rel, AccessShareLock); return relhash; }归档账本数据库你知道的那些事儿官方文档前提条件:系统中需要有审计管理员或者具有审计管理员权限的角色。数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。数据库已经正确配置审计文件的存储路径audit_directory。你不知道的那些事儿基本操作1、对指定用户历史表进行归档操作。omm=# SELECT pg_catalog.ledger_hist_archive('ledgernsp', 'usertable'); ledger_hist_archive --------------------- t (1 row) omm=# SELECT * FROM blockchain.ledgernsp_usertable_hist; rec_num | hash_ins | hash_del | pre_hash ---------+------------------+------------------+---------------------------------- 4 | e78e75b00d396899 | 84e8bfc3b974e9cf | 6475a497b7a272a92bab012d7f3d615b (1 row)主要步骤如下:Copy user history table.Do unify and truncate.sum all hash_ins and hash_del for unification.Do real truncate.heap_truncate_one_relDo insertion for unified row.simple_heap_insertFlush history hash table cache.2、执行全局区块表导出操作omm=# SELECT * FROM gs_global_chain; blocknum | dbname | username | starttime | relid | relnsp | relname | relhash | globalhash | txcommand ----------+--------+----------+-------------------------------+-------+-----------+-----------+------------------+----------------------------------+---------------- -------------------------------------------------------------- 1 | omm | omm | 2022-09-17 13:59:37.84824+00 | 16404 | ledgernsp | usertable | a41714001181a294 | 83927d11ba1fd678e8f4b0723a9cd5f2 | INSERT INTO led gernsp.usertable VALUES(1, 'alex'), (2, 'bob'), (3, 'peter'); 2 | omm | omm | 2022-09-17 13:59:51.723068+00 | 16404 | ledgernsp | usertable | b3a9ed0755131181 | b5ee73b6c20c817230182f6373c78e20 | UPDATE ledgerns p.usertable SET name = 'bob2' WHERE id = 2; 3 | omm | omm | 2022-09-17 13:59:58.159596+00 | 16404 | ledgernsp | usertable | 0ae4b4e4ed2fcab5 | 0cc9938cf7f1ed7f7f1a03c29954380a | DELETE FROM led gernsp.usertable WHERE id = 3; (3 rows) omm=# SELECT pg_catalog.ledger_gchain_archive(); ledger_gchain_archive ----------------------- t (1 row) omm=# SELECT * FROM gs_global_chain; blocknum | dbname | username | starttime | relid | relnsp | relname | relhash | globalhash | txcommand ----------+--------+----------+------------------------------+-------+-----------+-----------+------------------+----------------------------------+----------- 2 | omm | omm | 2022-09-17 13:59:37.84824+00 | 16404 | ledgernsp | usertable | 62a5b5ec53c47eca | 7252d09679b0b3836a2e63da17284ad5 | Archived. (1 row)gs_global_chain主要处理流程:Init and prepare bak dictionary.Using CopyStmt to copy global chain.Do unify and truncate.Using hash table to do unify, each hash_entry refers to one relid informations.Split gs_global_chain by relid, and accumulate rel_hash to a new record for each rel.Do rel truncate.Insert newest record to gchain order by relid.Flush global_hash cache.修复账本数据库你知道的那些事儿官方文档前提条件:系统中需要有审计管理员或者具有审计管理员权限的角色。数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。你不知道的那些事儿基本操作1、执行历史表修复操作omm=# select * from blockchain.ledgernsp_usertable_hist; rec_num | hash_ins | hash_del | pre_hash ---------+------------------+------------------+---------------------------------- 4 | e78e75b00d396899 | 84e8bfc3b974e9cf | 6475a497b7a272a92bab012d7f3d615b (1 row) omm=# SELECT pg_catalog.ledger_hist_repair('ledgernsp', 'usertable'); ledger_hist_repair -------------------- 0000000000000000 (1 row)[drawio] (rHmeQ8HWKS_RFXgP-oTUZINZguxBYqh2IV64Y0j5TAA.svg)2、执行全局区块表修复操作omm=# select * from gs_global_chain ; blocknum | dbname | username | starttime | relid | relnsp | relname | relhash | globalhash | txcommand ----------+--------+----------+------------------------------+-------+-----------+-----------+------------------+----------------------------------+----------- 2 | omm | omm | 2022-09-17 13:59:37.84824+00 | 16404 | ledgernsp | usertable | 62a5b5ec53c47eca | 7252d09679b0b3836a2e63da17284ad5 | Archived. (1 row) omm=# SELECT pg_catalog.ledger_gchain_repair('ledgernsp', 'usertable'); ledger_gchain_repair ---------------------- 62a5b5ec53c47eca (1 row)首先判断用户权限,之后通过get_gchain_relhash_sum函数计算relhash字段/* * get_gchain_relhash_sum -- calculate relhash from gs_global_chain * * relid: user table oid */ static uint64 get_gchain_relhash_sum(Oid relid) { uint64 relhash = 0; HeapTuple tuple = NULL; /* scan the gs_global_chain catalog by relid */ Relation gchain_rel = heap_open(GsGlobalChainRelationId, AccessShareLock); Form_gs_global_chain rdata = NULL; TableScanDesc scan = heap_beginscan(gchain_rel, SnapshotNow, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { rdata = (Form_gs_global_chain)GETSTRUCT(tuple); if (rdata == NULL || rdata->relid != relid) { continue; } relhash += rdata->relhash; } heap_endscan(scan); heap_close(gchain_rel, AccessShareLock); return relhash; }主要是计算并修复gs_global_chain中的relhash字段。总结账本数据库其实并不像我们想象的那么复制,实际上就是利用了区块链的最基本的原理,即当前记录的特征值 + 上一条记录特征值的hash值,再进行hash。下一条与上一条记录具有数据关联性,形成“链”的结构,如果篡改了其中的数据,则会导致“链”断开,导致不能与后面数据记录形成hash关联。_hist表记录了用户表每一步数据变化的过程,gs_global_chain表记录了所有防篡改模式下对用户表的操作记录。用户表结合_hist和global表就能完整记录和校验。
  • [技术干货] 【第四届openGauss征文活动参赛作品】openGauss Cluster Manager RTO Test
    一、环境介绍1. 软件环境类别版本下载链接备注OSopenEuler 20.03 (LTS)cid:link_1操作系统BenchmarkSQL5.0cid:link_3驱动版本:postgresql-9.3-1102.jdbc41.jar模拟TPCC压力的程序驱动为程序自带的pg驱动Golanggo1.18 linux/arm64cid:link_5cid:link_2cid:link_4模拟应用连接的程序openGauss3.0.0cid:link_0数据库2. 硬件环境主机CPU规格硬盘职责node1Kunpeng-920虚拟机16c/64g通用型SSD主库node2Kunpeng-920虚拟机16c/64g通用型SSD同步备库go程序node3Kunpeng-920虚拟机16c/64g通用型SSD异步备库BenchmarkSQL程序3. 架构图二、测试场景1. go程序多IP连接测试RTO原理:CM检测主库发生故障,不可访问时会自动选新主,go驱动通过target_session_attrs=read-write控制只连主库,通过SQL select sysdate,pg_is_in_recovery(); 查询结果时间戳查看RTOgo程序代码[root@cloud001-0003 go]# cat 1.go // Copyright © 2021 Bin Liu package main import ( "database/sql" "fmt" _ "gitee.com/opengauss/openGauss-connector-go-pq" "log" "os" "os/signal" "syscall" "time" ) /* 需要有访问dbe_perf.global_instance_time的权限 CREATE USER dbuser_monitor with login monadmin PASSWORD 'Mon@1234'; grant usage on schema dbe_perf to dbuser_monitor; grant select on dbe_perf.global_instance_time to dbuser_monitor; CGO_ENABLED=0 GOOS=linux GOARCH=arm64 */ var ( dsnExample = `DSN="postgres://gaussdb:secret@foo,bar,baz/mydb?sslmode=disable" DSN="postgres://gaussdb:secret@foo:1,bar:2,baz:3/mydb?sslmode=disable" DSN="user=gaussdb password=secret host=foo,bar,baz port=5432 dbname=mydb sslmode=disable" DSN="user=gaussdb password=secret host=foo,bar,baz port=5432,5432,5433 dbname=mydb sslmode=disable"` ) func main() { os.Setenv("DSN", "postgres://gaussdb:Enmo12345@172.16.0.65:26000,172.16.0.202:26000,172.16.0.193:26000/postgres?"+ "sslmode=disable&loggerLevel=debug&target_session_attrs=read-write") connStr := os.Getenv("DSN") if connStr == "" { fmt.Println("please define the env DSN. example:\n" + dsnExample) return } fmt.Println("DNS:", connStr) db, err := sql.Open("opengauss", connStr) if err != nil { log.Fatal(err) } var ( newTimer = time.NewTicker(1 * time.Second) doClose = make(chan struct{}, 1) ) go func() { for { select { case <-newTimer.C: if err := getNodeName(db); err != nil { fmt.Println(err) } case <-doClose: newTimer.Stop() return } } }() sigChan := make(chan os.Signal, 2) signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL) //nolint:staticcheck defer signal.Stop(sigChan) <-sigChan doClose <- struct{}{} } func getNodeName(db *sql.DB) error { var err error // tx, err := db.Begin() // if err != nil { // return err // } // defer tx.Commit() var sysdate string var pgIsInRecovery bool var nodeName string err = db.QueryRow("select sysdate,pg_is_in_recovery();"). Scan(&sysdate, &pgIsInRecovery) if err != nil { return err } var channel string // err = db.QueryRow("select channel from pg_stat_get_wal_senders() limit 1 "). // Scan(&channel) fmt.Println(sysdate, nodeName, pgIsInRecovery, channel) // if err != nil { // return err // } return nil } 模拟数据库故障[omm@cloud001-0002 data]$ mv db1/ db1.bakgo程序连接数据库及重连时间时间差 ```s 2022/04/11 16:02:13.614273 connector.go:222: info dialing server host 172.16.0.65 port 26000 2022/04/11 16:02:20.683716 connector.go:145: debug find instance host 172.16.0.202 port 26000RTO时间7s ## 2. BenchmarkSQL多IP连接测试RTO > 原理:CM检测主库发生故障,不可访问时会自动选新主,jdbc驱动通过target_session_type=master控制只连主库,通过SQL 程序执行时间戳查看RTO **BenchmarkSQL模拟负载及重连时间** ![](https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbstemp/20221010/cmtybbs/002/54e/1fe/05b5e1232d00254e1fe6c017a981df34.20221010064417.22465626059862641794283274131784:20221010074417:2400:25ECFB3EAB696C25B6BF32DEF9562B6C0DDF9F99D7F9AAB2DE38A263EA3F531D.png) ![](https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbstemp/20221010/cmtybbs/002/54e/1fe/05b5e1232d00254e1fe6c017a981df34.20221010064447.94841903110897844984354683484747:20221010074447:2400:0F348177270F1B142E52AC69DCBD7F52F0BDED979B0E27D5346E2CEE79DA32F6.png) - 时间差 ```s 16:02:13,561 [Thread-8] ERROR jTPCCTData : Unexpected SQLException in STOCK_LEVELsage: 153MB / 897MB 16:02:20,834 [Thread-57] FATAL jTPCCTerminal : Unexpected SQLException on rollback: This connection has been closed. RTO时间7.273s3. 数据库端观测时间数据库日志时间差2022-04-11 16:02:13.253 tid=1795056 StartAndStop ERROR: data path disc writable test failed, /opt/mogdb/data/data/db1. 2022-04-11 16:02:20.438 tid=1815697 LOG: failover msg from cm_server, data_dir :/opt/mogdb/data/data/db1 nodetype is 2 RTO时间7.185s总结在有负载情况(tpcc压测产生负载,数据库服务器CPU占用50%左右)进行主库宕机测试,以主库宕机为起始点,备库成功作为新主库启动成功为终止点RTO为7.185s以主库宕机为起始点,模拟TPCC压测的benchmarkSQL程序成功重新连接到新主库为终止点RTO为7.273s以主库宕机为起始点,模拟其它应用连接数据库的go程序成功重新连接到新主库为终止点7s由于go程序至精确到s,猜测实际时间为7.185s以上综上所述openGauss Cluster Manager RTO约为7s左右
  • [技术干货] 【第四届openGauss征文活动参赛作品】干货输出【开源Mall4j商城系统-MySQL5.7数据库底座替换探索实践】
    系统介绍Mall4j是国内领先的电商商城系统源码提供商,是众多知名企业搭建商城的不二选择。Mall4j商城系统支持企业自营模式,招商模式,O2O门店模式,满足企业各个发展阶段的商业模式,全面覆盖微信商城、小程序商城、手机触屏版商城、苹果APP商城、安卓APP商城及PC端,统一的数据,能很大程度提高销售利润和提升服务质量,让您一次开店,全通路覆盖。Mall4j商城系统可以将企业的资源进行有效的整合,让商家的布局更加广泛、配套更加完善、管理更加成熟。另外,mall4j商城系统可以帮助企业集合各个渠道的资源,充分利用资源和渠道来提升商城的销量。还有多种营销功能:团购、秒杀、会员积分、优惠券、满减满折等营销玩法,把消费变成了一种“游戏”,有趣,有效果。系统亮点Mall4j商城系统功能亮点: 1、系统具备高效的管理功能:商品管理、会员管理、订单管理、统计管理、支付管理、门店管理、库存管理等管理功能; 2、灵活多样的营销赋能模式:满减、团购、秒杀、分销、积分、O2O、优惠券、套餐、赠品等营销方式; 3、多终端适配:支持PC、H5、小程序、APP(android、IOS)。Mall4j商城系统技术亮点:技术主流,使用最流行框架技术,没有过多的技术债务。提供全部源码,无封装无加密,没有license。更新升级方便,通过git私服进行代码合并更新。代码前后端分离,代码质量高注释清晰,方便二次开发。mall4j商城版本开源版银河版宇宙版白洞版业务模式B2CB2CB2B2CS2B2C适用群体个人、初学者自营店、小规模企业自营+入驻、中小型企业自营+供应商/商家入驻加密程度全开源,无加密提供全部源码,无加密提供全部源码,无加密提供全部源码,无加密服务内容商业授权AGPL3.0协议永久授权永久授权永久授权代码更新Gitee、Github更新Git私服更新Git私服更新Git私服更新企业售后群×√√√功能列表√√√√技术文档√√√√操作手册×√√√合同签约×√√√基础功能前后端分离√√√√Spring Cloud脚手架√××√Spring Boot脚手架√√√√权限管理√√√√完整下单流程√√√√小程序端√√√√H5端√√√√PC端×√√√App端(iOS、Android)×√√√后台管理移动端××√×后台管理PC端√√√√国际化×√√×高级功能多商家入驻√×√√供应商端×××√平台抽佣××√√PC端装修××√√移动端装修××√√优惠券×√√√满减满折×√√√拼团活动×√√√秒杀活动×√√√商品预售×√√√优惠套餐××√√赠品××√√分销模块×√√√会员管理×√√√积分商城×√√√虚拟商品××√√用户自提×√√×同城配送×√√×小程序直播×√√√客服系统(IM)×√√√进销存管理××√√客户标签与营销×√√√数据分析×√√√数据报表×√√√消息推送×√√√技术组件技术版本说明Spring Boot2.1.6.RELEASEMVC核心框架Spring Security oauth22.1.5.RELEASE认证和授权框架MyBatis3.5.0ORM框架MyBatisPlus3.1.0基于mybatis,使用lambda表达式的Swagger-UI2.9.2文档生产工具Hibernator-Validator6.0.17.Final验证框架redisson3.10.6对redis进行封装、集成分布式锁等hikari3.2.0数据库连接池log4j22.11.2更快的log日志工具fst2.57更快的序列化和反序列化工具orika1.5.4更快的bean复制工具lombok1.18.8简化对象封装工具hutool4.5.0更适合国人的java工具集swagger-bootstrap1.9.3基于swagger,更便于国人使用的swagger u工具版本jdk1.8+mysql5.7+redis3.2+技术架构SOA会通过ESB来作为系统和服务之间的通信桥梁,ESB本身还提供服务地址的管理、不同系统之间的 协议转化和数据格式转化等等。消费者不需要关心目标的服务位置,实现了服务消费者和服务生产者的高度解耦,SOA可以消除信息孤岛并实现共享业务重用。Mall4j商城系统则是使用了比起SOA还要灵活好用的微服务架构,微服务关注的是解耦,努力降低业务之间的耦合度,实现更多服务的复用,在DevOps有更大自由程度的持续交付。Mall4j商城系统的微服务技术包括Spring Cloud API网关、服务注册与发现、配置中心、负载均衡、分布式事务,其它相关技术包括有容器化技术、轻量级虚拟化、容器快速启停、易管理扩展、版本控制、应用安全隔离Mall4j商城系统应用使用范围电商系统B2C商城系统 【品牌电商】 单用户商城系统,统一后台管理、多终端覆盖B2B2C商城系统【平台电商】 多用户商城系统,自营+多商家入驻电商平台S2B2C商城系统【供应链】 融合供应链、为供应商、经销商、零售商赋能O2O商城系统【新零售】 线上线下融合,助力传统零食企业转型与发展应用终端电商平台【PC端、可视化】 可视化编辑,完善购物体验及商品数据分析商城小程序【小程序】 轻应用,体验优质,快速抢占移动电商市场商城APP【UNIAPP】 IOS、Android双APP,聚拢用户购物更便捷H5商城【HTML5】 手机浏览器,微信商城、随时随地五单支付。业务场景社交电商【移动电商】 融合拼团、会员分销等多种前沿社交营销模式积分商城【留存复购】 结合会员体系 、打造积分运营体系,引老留新跨境电商【海外】 海外支付,多语言切换,为企业提供跨境方案直播电商【引流获客】 微信小程序直播+电商模式,低成本变现流量Mall4j商城系统替换因素考虑Mall4j商城系统复杂,涉及的技术组件多,我们准备用OpenGauss替换MySQL5.7,改动的的东西涉及项目依赖文件、配置文件、解析文件,尤其数据库的内核能力以及对 应用框架的兼容 程度非常重要,假设开发者要大费周章大面积的改动,或者要动jar包的配置,估计OpenGauss替换MySQL5.7的计划就要搁浅或者放弃了。代码结构[root@enmoedu1 yami-b2b2c]# tree -L 1 . ├── CHANGELOG.md ├── compile.sh ├── db ├── doc ├── docker-compose.yml ├── LICENSE ├── log ├── mall4m ├── mall4uni ├── mall4v ├── pom.xml ├── README.md ├── screenshot ├── yami-shop-admin 商城后端接口服务管理系统,数据库操作有关 ├── yami-shop-api 商城前端接口调用系统,数据库操作有关 ├── yami-shop-bean ├── yami-shop-common pom.xml含有mysql的调用 ├── yami-shop-quartz ├── yami-shop-security ├── yami-shop-service └── yami-shop-sys src/main/resources/mapper/SysMenuMapper.xml 数据库操作有关 15 directories, 6 files pom.xml/opt/projects/yami-b2b2c/yami-shop-common目录, pom.xml内容如下,关于mysql的jdbc需要注释 改成 org.postgresql postgresql 42.2.2 logback-prod.xml/opt/projects/yami-b2b2c/yami-shop-admin/src/main/resources/logback/logback-prod.xml/opt/projects/yami-b2b2c/yami-shop-api/src/main/resources/logback/logback-prod.xml里面确定唯一的PROJECT_PATHapplication-dev.yml与 application-prod.ymldev是开发环境的配置参数 prod是生产环境的配置参数/opt/projects/yami-b2b2c/yami-shop-admin/src/main/resources/里面有两个文件application-dev.yml 和 application-prod.yml/opt/projects/yami-b2b2c/yami-shop-api/src/main/resources里面有两个文件application-dev.yml 和application-prod.yml把原来的注释,新的JDBC连接串如下 url: jdbc:postgresql://XXXXXX:15400/mytest username: henley password: XXXX driver-class-name: org.postgresql.Driver编译打包准备对代码进行编译打包,只需要在主代码下运行以下命令,这边主代码路径是/opt/projects/yami-b2b2c,当前目录下执行mvn clean package -DskipTests成功执行后如下所示自动在下面两处地方生成jar包,一个是商城后台接口,一个是商城前端接口/opt/projects/yami-b2b2c/yami-shop-admin/target/yami-shop-admin-0.0.1-SNAPSHOT .jar/opt/projects/yami-b2b2c/yami-shop-api/target/yami-shop-api-0.0.1-SNAPS HOT.jar开发调试 以开发的模式运行以下两个后端服务,可以获取DEBUG日志-Dspring.profiles.active=dev 意味着用开发模式运行 -Dspring.profiles.active=prod 意味着用生产模式运行以下命令启动后台服务,运行成功会发现8085端口打开nohup java -jar -Dspring.profiles.active=dev "/opt/projects/yami-b2b2c/yami-shop-admin/target/yami-shop-admin-0.0.1-SNAPSHOT.jar" > "/opt/projects/yami-b2b2c/yami-shop-admin/target/log/yami-shop-admin-console.log" &以下命令启动前端服务,运行成功会发现8086端口打开nohup java -jar -Dspring.profiles.active=dev "/opt/projects/yami-b2b2c/yami-shop-api/target/yami-shop-api-0.0.1-SNAPSHOT.jar" > "/opt/projects/yami-b2b2c/yami-shop-api/target/log/yami-shop-api-console.log" &查看控制台日志输出后台服务日志tail -f ${PROJECT_PATH}/log/admin.log前端服务日志tail -f ${PROJECT_PATH}/log/api.log启动后端服务管理平台进入 mall4v-master管理目录 [root@enmoedu1 mall4v-master]# npm run dev直接访问cid:link_0 ,它会直接与后台服务8085以及前端服务8086连线。数据库梳理ER图Mall4j一共有56个表示例MySQL表相关56个表都要做不同程度的修改,要修改替换的地方,举例MySQL表。CREATE TABLE `qrtz_job_details` ( `SCHED_NAME` varchar(120) unsigned NOT NULL AUTO_INCREMENT , `JOB_NAME` varchar(200) NOT NULL, `JOB_GROUP` varchar(200) NOT NULL, `DESCRIPTION` varchar(250) DEFAULT NULL, `JOB_CLASS_NAME` varchar(250) NOT NULL, `IS_DURABLE` varchar(1) NOT NULL, `IS_NONCONCURRENT` varchar(1) NOT NULL, `IS_UPDATE_DATA` varchar(1) NOT NULL, `REQUESTS_RECOVERY` varchar(1) NOT NULL, `JOB_DATA` blob, `JOB_tinyint` tinyint(2), `JOB_smallint` smallint(10), `JOB_int` int(10), `JOB_bigint` bigint(10), file_path varchar(255) DEFAULT NULL COMMENT '文件路径', file_type varchar(20) DEFAULT NULL COMMENT '文件类型', upload_time datetime DEFAULT NULL COMMENT '上传时间', `JOB_DATA_double` double(12,2), PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`), KEY `IDX_QRTZ_J_REQ_RECOVERY` (`SCHED_NAME`,`REQUESTS_RECOVERY`), KEY `IDX_QRTZ_J_GRP` (`SCHED_NAME`,`JOB_GROUP`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 数据类型数据类型的改造是一段工作量blob 改成 bytea tinyint(2) 改成 smallint smallint(10) 改成 smallint int(10) 改成 int bigint(10) 改成 bigint double(12,2) 改成 double precision datetime 改成 date数据索引openGauss的数据索引与MySQL的不一样 create index IDX_QRTZ_J_REQ_RECOVERY on qrtz_job_details (SCHED_NAME,REQUESTS_RECOVERY); create index IDX_QRTZ_J_GRP on qrtz_job_details(SCHED_NAME,JOB_GROUP);数据注释openGauss的数据注释与MySQL的不一样comment on column qrtz_job_details.file_path is '文件路径'; comment on column qrtz_job_details.file_type is '文件类型';自增ID表openGauss的自增ID与MySQL不一样 ,与主流的Postgresql的一模一样,它用自己的序列函数。GRANT ALL PRIVILEGES ON TABLE tz_sys_log TO henley; create sequence public.tz_sys_log_id start with 846 increment by 1 no minvalue no maxvalue cache 1; alter sequence public.tz_sys_log_id owner to henley; alter table tz_sys_log alter column id set default nextval('public.tz_sys_log_id');UUID表tz_user表有user_id使用的是uuidopenGauss要支持uuid,可以通过FUNCTION,下面定义sys_guid的FUNCTIONmytest=# \sf sys_guid CREATE OR REPLACE FUNCTION pg_catalog.sys_guid() RETURNS character varying LANGUAGE sql NOT FENCED NOT SHIPPABLE AS $function$ select upper(md5(random()::text || clock_timestamp()::text)) $function$; mytest=# select sys_guid() ; sys_guid ---------------------------------- 72F965C6E7FC0F5D547787E969D5596F (1 row) mytest=# select sys_guid() ; sys_guid ---------------------------------- 3FBC1EC7357AC11BA0B06F4D116E051A (1 row) mybatis的SQL表达XML由于openGauss语法对符号 不识别,要把 的特殊符号都去掉 /opt/projects/yami-b2b2c/yami-shop-sys/src/main/resources/mapper/SysMenuMapper.xmlSysMenuMapper.xml 把 `` 的特殊符号都去掉。举例一个增加会员的涉及的数据库相关的修改操作现在在后台管理页面,点击管理员列表,再单击新增, 直接转发请求到后端服务yami-shop-admin,会触发数据库新增数据查看后端服务日志 tailf /opt/projects/yami-b2b2c/yami-shop-admin/target/log/yami-shop-admin-console.log### Error updating database. Cause: org.postgresql.util.PSQLException: ERROR: permission denied for relation tz_sys_user Detail: N/A ### The error may exist in com/yami/shop/sys/dao/SysUserMapper.java (best guess) ### The error may involve com.yami.shop.sys.dao.SysUserMapper.insert-Inline ### The error occurred while setting parameters ### SQL: INSERT INTO tz_sys_user ( username, password, email, mobile, status, shop_id, create_time ) VALUES ( ?, ?, ?, ?, ?, ?, ? ) ### Cause: org.postgresql.util.PSQLException: ERROR: permission denied for relation tz_sys_user Detail: N/A ; bad SQL grammar []; nested exception is org.postgresql.util.PSQLException: ERROR: permission denied for relation tz_sys_user Detail: N/A以上错误主要是由于没有授权,通过超管进入 mytest数据库openGauss=# \c mytest; Non-SSL connection (SSL connection is recommended when requiring high-security) You are now connected to database "mytest" as user "omm". mytest=# GRANT ALL PRIVILEGES ON TABLE tz_sys_user TO henley;继续查看yami-shop-admin-console.log,报错变成ERROR: null value Caused by: org.postgresql.util.PSQLException: ERROR: null value in column "id" violates not-null constraint Detail: Failing row contains (null, 3, 1). at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2553) at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2285) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:323) at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:481) at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:401) at 以上报错ERROR: null value 原因是自增ID的问题,通过以下方法实现自增ID。create sequence public.tz_sys_user_id start with 2 increment by 1 no minvalue no maxvalue cache 1; alter sequence public.tz_sys_user_id owner to henley; alter table tz_sys_user alter column user_id set default nextval('public.tz_sys_user_id'); **tz_sys_user 表与 tz_sys_user_role表、tz_sys_log表有关联,同样需要以下授权 **GRANT ALL PRIVILEGES ON TABLE tz_sys_user_role TO henley; create sequence public.tz_sys_user_role_id start with 1 increment by 1 no minvalue no maxvalue cache 1; alter sequence public.tz_sys_user_role_id owner to henley; alter table tz_sys_user_role alter column id set default nextval('public.tz_sys_user_role_id');注意tz_sys_log表已经有845条数据,所以新增的数据从846开始算。GRANT ALL PRIVILEGES ON TABLE tz_sys_log TO henley; create sequence public.tz_sys_log_id start with 846 increment by 1 no minvalue no maxvalue cache 1; alter sequence public.tz_sys_log_id owner to henley; alter table tz_sys_log alter column id set default nextval('public.tz_sys_log_id');创建用户成功后 mytest=# select * from tz_sys_log where id = 846;最后总结开源Mall4j商城系统的数据库底座替换,把MySQL改成OpenGauss,对业务逻辑相关的代码侵入不大,主要工作量是数据库表结构方面重构,OpenGauss对微服务的功能是支持的。
  • [技术干货] 【第四届openGauss征文活动参赛作品】干货输出【SpringBoot + OpenGauss3开发入门】
    本文介绍如何快速安装OpenGauss3,OpenGauss3的安装这是笔者浓缩提炼的,并且在SpringBoot中集成使用OpenGauss3数据库。单机版openGauss3快速环境安装groupadd dbgroup useradd -g dbgroup omm # 可后面安装时创建 passwd omm #设置密码为Gauss_1234创建安装程序目标目录mkdir /home/omm/opengauss3 chown -R omm:dbgroup /home/omm/opengauss3下载opengauss3.00mkdir /opengauss3 cd /opengauss3 wget https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.0.0/x86/openGauss-3.0.0-CentOS-64bit-all.tar.gz解压文件tar -zvxf openGauss-3.0.0-CentOS-64bit-all.tar.gz tar zxvf openGauss-3.0.0-CentOS-64bit-cm.tar.gz tar zxvf openGauss-3.0.0-CentOS-64bit-om.tar.gz设置opengauss集群配置文件,这里设单点安装[root@enmoedu1 opengauss3]# cat cluster_config.xml "/> 前置系统软件包yum install -y epel-release yum install -y bzip2 # 安装bzip2用于后面的解压openGauss安装包 sed -i 's/源IP/目标IP/g' cluster_config.xml sed -i 's/hdp1/你的主机名/g' cluster_config.xml 初始化系统安装配置参数,以必须管理员root的权限运行,进入opengauss3运行初始化程序[root@hdp1 ~]# cd /opengauss3/ [root@hdp1 opengauss3]# ./script/gs_preinstall -U omm -G dbgroup -X ./cluster_config.xml Parsing the configuration file. Successfully parsed the configuration file. Installing the tools on the local node. Successfully installed the tools on the local node. Setting host ip env Successfully set host ip env. Are you sure you want to create the user[omm] (yes/no)? no Preparing SSH service. Successfully prepared SSH service. Checking OS software. Successfully check os software. Checking OS version. Successfully checked OS version. Creating cluster's path. Successfully created cluster's path. Set and check OS parameter. Setting OS parameters. Successfully set OS parameters. Warning: Installation environment contains some warning messages. Please get more details by "/opengauss3/script/gs_checkos -i A -h hdp1 --detail". Set and check OS parameter completed. Preparing CRON service. Successfully prepared CRON service. Setting user environmental variables. Successfully set user environmental variables. Setting the dynamic link library. Successfully set the dynamic link library. Setting Core file Successfully set core path. Setting pssh path Successfully set pssh path. Setting Cgroup. Successfully set Cgroup. Set ARM Optimization. No need to set ARM Optimization. Fixing server package owner. Setting finish flag. Successfully set finish flag. Preinstallation succeeded.下面要以omm的用户正式运行安装程序,首先必须把权限赋给ommchown -R omm:dbgroup /opengauss3 切换到 omm,在/opengauss3目录下运行安装目录 [root@hdp1 opengauss3]# su omm [omm@hdp1 opengauss3]$ ./script/gs_install -X ./cluster_config.xml Parsing the configuration file. Check preinstall on every node. Successfully checked preinstall on every node. Creating the backup directory. Successfully created the backup directory. begin deploy.. Installing the cluster. begin prepare Install Cluster.. Checking the installation environment on all nodes. begin install Cluster.. Installing applications on all nodes. Successfully installed APP. begin init Instance.. encrypt cipher and rand files for database. Please enter password for database: Please repeat for database: begin to create CA cert files The sslcert will be generated in /home/omm/opengauss3/install/app/share/sslcert/om NO cm_server instance, no need to create CA for CM. Cluster installation is completed. Configuring. Deleting instances from all nodes. Successfully deleted instances from all nodes. Checking node configuration on all nodes. Initializing instances on all nodes. Updating instance configuration on all nodes. Check consistence of memCheck and coresCheck on database nodes. Configuring pg_hba on all nodes. Configuration is completed. Successfully started cluster. Successfully installed application. end deploy..验证服务进程是否激 活[root@hdp1 ~]# ps -eaf | grep omm root 14898 32160 0 15:55 pts/1 00:00:00 su omm omm 14899 14898 0 15:55 pts/1 00:00:00 bash omm 19411 1 9 16:08 ? 00:00:02 /home/omm/opengauss3/install/app/bin/gaussdb -D /home/omm/opengauss3/install/data/dn root 19784 360 0 16:09 pts/2 00:00:00 grep --color=auto omm命令行登录gsql -d postgres -p 15400 安装opengauss3注意事项之前安装mogdb,影响了opengauss3的环境,/home/omm/.bashrc 里面记录了安装后的变量,如果要卸载opengauss,必须要把.bashrc 下面所有的东西都去掉。# User specific aliases and functions export GPHOME=/home/omm/opengauss3/install/om export PATH=$GPHOME/script/gspylib/pssh/bin:$GPHOME/script:$PATH export LD_LIBRARY_PATH=$GPHOME/lib:$LD_LIBRARY_PATH export PYTHONPATH=$GPHOME/lib export GAUSSHOME=/home/omm/opengauss3/install/app export PATH=$GAUSSHOME/bin:$PATH export LD_LIBRARY_PATH=$GAUSSHOME/lib:$LD_LIBRARY_PATH export S3_CLIENT_CRT_FILE=$GAUSSHOME/lib/client.crt export GAUSS_VERSION=3.0.0 export PGHOST=/home/omm/opengauss3/tmp export GAUSSLOG=/var/log/omm/omm umask 077 export GAUSS_ENV=2 export GS_CLUSTER_NAME=dbClusterspringBoot应用集成OpenGaussSOA是一种粗粒度、松耦合服务架构,服务之间通过简单、精确定义接口进行通讯,不涉及底层编程接口和通讯模型。SOA可以看作是B/S模型、XML(标准通用标记语言的子集)/Web Service技术之后的自然延伸,面向服务架构,它可以根据需求通过网络对松散耦合的粗粒度应用组件进行分布式部署、组合和使用。服务层是SOA的基础,可以直接被应用调用,从而有效控制系统中与软件代理交互的人为依赖性。简而言之SOA可以消除信息孤岛并实现共享业务重用,我们通过SOA可以打造下图的复杂系统,其中蓝色用户服务 ,我们可以通过springboot + OpenGauss 技术实现。我们使用OpenGauss作为具体数据存储,使用开发工具创建一个数据库mysqltest,并在mysqltest数据库中创建一张表userennity和user1,创建语句如下:create table userentity( id int , username varchar(50), password varchar(50), user_sex varchar(10), nick_name varchar(50) ); create table user1( id int , name varchar(50), password varchar(50)); DEMO代码 +---src | +---main | | +---java | | | ---com | | | ---main | | | +---controler 具体业务逻辑 | | | +---mapper 定义实现DAO的CRUD实体操作 | | | +---model 实体类 | | | ---service 实现服务类注意UserControler是首先调用的service,继而去调用实体操作。 而UserEntityControler是通过mapper的封装去调用 DAO的CRUD的操作,如下无论是UserControler还是 UserEntityControler 都需要底层数据库对应用支持友好。 确定opengauss的用户、密码、端口及相关IP启动服务服务正在运行中 查看用户实体1查看用户实体2源代码下载:链接:cid:link_0 提取码:5wjdspringboot集成opengauss的FAQ用户名/密码不对spring报错### The error may involve com.main.mapper.UserMapper.getAll ### The error occurred while executing a query ### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is org.postgresql.util.PSQLException: 不明的原因导致驱动程序造成失败,请回报这个例外。] with root cause java.lang.NullPointerException: null而opengauss内部执行报错[omm@enmoedu1 ~]$ gsql -U henley -h 192.168.30.65 -p 15400 Password for user henley: gsql: FATAL: Invalid username/password,login denied.根本原因分析 openGauss默认是sha256,而登录则设成只允许md5登录,所以一直识用户名和密码错误解决方法及步骤 vi /home/omm/opengauss3/install/data/dn/postgresql.conf 修改设置 encryption_type = 1vi /home/omm/opengauss3/install/data/dn/pg_hba.conf增加设置 host all henley 0.0.0.0/0 md5用户没有对表的操作权限spring报错org.postgresql.util.PSQLException: ERROR: permission denied for relation userentity 详细:N/Aopengauss报错mytest=> SELECT id, userName, passWord, user_sex, nick_name FROM userentity; ERROR: permission denied for relation userentity DETAIL: N/A解决方法及步骤以postgres的身份登录root[omm@enmoedu1 ~]$ gsql -d postgres -p 15400 gsql ((openGauss 3.0.0 build 02c14696) compiled at 2022-04-01 18:12:34 commit 0 last mr ) Non-SSL connection (SSL connection is recommended when requiring high-security) Type "help" for help.切换到指定的数据库openGauss=# \c mytest; Non-SSL connection (SSL connection is recommended when requiring high-security) You are now connected to database "mytest" as user "omm".执行授权mytest=# GRANT ALL PRIVILEGES ON userentity TO henley; GRANT授权后能够正常,但是发现一个问题,现在我们是通过Postgresql的jdbc驱动去访问OpenGauss的,OpenGauss没有自己的原生jdbc驱动吗?答案是有的,而且还支持maven方式,见下。 但是笔者的运气很差,通过maven一直无法下载openGauss的core包,只能通过手动的方式下载。wget https://opengauss.obs.cn-south-1.myhuaweicloud.com/ 3.0.0/x86/openGauss-3.0.0-JDBC.tar.gz再在idea把jar包引入进来,引入步骤 Project Structure --> Project Settings --> Libraries --> Add(alt +insert) --> applyapplication.properties稍微修改一下 ==spring.datasource.url=jdbc:opengauss://192.168.30.65:15400/mytest== ==spring.datasource.driver-class-name=org.opengauss.Driver==#spring.datasource.url=jdbc:postgresql://XXXX:5432/mytest #spring.datasource.url=jdbc:postgresql://XXXX:15400/mytest spring.datasource.url=jdbc:opengauss://XXXX:15400/mytest spring.datasource.username=henley spring.datasource.password=XXXX spring.datasource.driver-class-name=org.opengauss.Driver #spring.datasource.driver-class-name=org.postgresql.Driver ### mybatis config ### mybatis.config-locations=classpath:mybatis/mybatis-config.xml mybatis.mapper-locations=classpath:mybatis/mapper/*.xml mybatis.type-aliases-package=com.main.model最后总结openGauss对业界知名的spring支持还算友好,直接用传统的postgresql驱动就可以接入使用,也有自己的opengauss驱动。如果使用顺利,还可以支持分布式配置、服务路由、负载均衡、熔断限流、链路监控这些功能,事实上在微服务的技术框架上也是支持的。
  • 【第四届openGauss征文活动参赛作品】SSM+MySQL替换探索 opengauss3对比postgresql12
    SSM介绍SSM(Spring+SpringMVC+MyBatis)框架集由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部分内容),常作为数据源较简单的web项目的框架。SpringSpring就像是整个项目中装配bean的大工厂,在配置文件中可以指定使用特定的参数去调用实体类的构造方法来实例化对象。也可以称之为项目中的粘合剂。 Spring的核心思想是IoC(控制反转),即不再需要程序员去显式地new一个对象,而是让Spring框架帮你来完成这一切。SpringMVCSpringMVC在项目中拦截用户请求,它的核心Servlet即DispatcherServlet承担中介或是前台这样的职责,将用户请求通过HandlerMapping去匹配Controller,Controller就是具体对应请求所执行的操作。SpringMVC相当于SSH框架中struts。mybatismybatis是对jdbc的封装,它让数据库底层操作变的透明。mybatis的操作都是围绕一个sqlSessionFactory实例展开的。mybatis通过配置文件关联到各实体类的Mapper文件,Mapper文件中配置了每个类对数据库所需进行的sql语句映射。在每次与数据库交互时,通过sqlSessionFactory拿到一个sqlSession,再执行sql命令。 页面发送请求给控制器,控制器调用业务层处理逻辑,逻辑层向持久层发送请求,持久层与数据库交互,后将结果返回给业务层,业务层将处理逻辑发送给控制器,控制器再调用视图展现数据。SSM搭配的数据库无庸置疑,SSM经常搭配的数据库是MySQL或者是Postgresql,而国内使用MySQL的人比 Postgresql的人多, 所以本文主要内容关于SSM DEMO已经搭配用上MySQL,系统本身可以注册写入数据入库,可以从库中读取数据进行登录。SSM demo代码经过测试对比,SSM DEMO代码基于三个不同的数据库,除了JDBC连接串不同,如下。另外最大的不同就是数据库的ID自增机制不同,ID自增机制是数据库的一项基本功能,我们非常重视这一点,这个不同会不会导致DEMO代码要做相关的适配,好听叫做适配,不好听叫做业务侵入。我们现在开发的 SSM DEMO代码是在MySQL的基础上开发,优先满足了MySQL。Postgresql如果把MySQL改换成Postgresql ,运行程序的会报错,如下org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.jdbc.UncategorizedSQLException: ### Error updating database. Cause: org.postgresql.util.PSQLException: Returning autogenerated keys is only supported for 8.2 and later servers. ### SQL: insert into user1 (username, password, age) values (?, ?, ?) ### Cause: org.postgresql.util.PSQLException: Returning autogenerated keys is only supported for 8.2 and later servers.笔者用的是postgresql12,经过网上查阅 ,mybatis的定义配置必须要更改。 ==useGeneratedKeys="true" keyProperty="id"摘掉==改换成 重新在idea运行tomcat9如下,发现数据能够写入了,但是写入的数据没有id列为空值。mytest=# select * from user1; id | username | password | age ----+-----------+-----------+----- 1 | user1 | password1 | 18 | hexin.xue | cxc | 25这是由于postgresql没有自动增加id机制的功能,所以数值一直为空。 如果要实现自增ID,它是通过sequence去实现增加ID的。两个方法是给相关表增加sequence,一种是已建表的基础上增加sequence实现增加IDcreate sequence public.userid_seq start with 1 increment by 1 no minvalue no maxvalue cache 1; alter sequence public.userid_seq owner to henley; alter table user1 alter column id set default nextval('public.userid_seq'); insert into user1 (username, password, age) values ('username1', 'password1', 100);另外一种重建表,建表就实现CREATE SEQUENCE sq_user_id START 1 INCREMENT 1 CACHE 20; create table user1(id int NOT NULL DEFAULT nextval('sq_user_id') , username varchar(50),password varchar(50),age int);建表后如果还有问题 ,下面再补刀alter sequence public.sq_user_id owner to henley; alter table user1 alter column id set default nextval('public.sq_user_id');增加sequence后,写入数据后ID例有值了。OpenGauss依然是同样代码,把jdbc的连接串改成OpenGauss, 我们启动tomcat,加载服务,我们惊喜的发现没有报错。 此处在postgresql12需要更改,在opengauss3不需要任何更改,spring指向的应用层没有报错。但是数据库底层ID列的数据仍然空值,OpenGauss也和Postgresql一样,都是用sequence 去实现ID的自增长。create sequence public.userid_seq start with 1 increment by 1 no minvalue no maxvalue cache 1; alter sequence public.userid_seq owner to henley; alter table user1 alter column id set default nextval('public.userid_seq');**体验源代码: **链接:cid:link_0 提取码:0kwg最后总结这是SSM开发中比较低端的DEMO代码,但是从中可见openGauss用心的包容,在接口层适配了mybatis,遇到自增ID提高了容错性,相对于postgresql多了一道方便。
  • [技术干货] 【第四届openGauss征文活动参赛作品】基于openGauss的五子棋AI项目
    1 前言openGauss是一款全面友好开放的企业级开源关系型数据库。openGauss采用木兰宽松许可证v2发行,提供面向多核架构的极致性能、全链路的业务、数据安全、基于AI的调优和高效运维的能力。本文采用openGauss设计一个AI小demo。2 方案意义人工智能被广泛用于棋类对弈的主要原因是:棋类对弈自古以来就被认为是人类智力活动的象征,若人工智能成功达到、甚至高于人类水平,则就代表AI的发展潜力,从而吸引更多研究者关注并投身其中; 棋类很适合作为新AI算法的标杆。棋类游戏规则简洁、输赢都在盘面,适合计算机求解。理论上只要在计算能力和算法上有新的突破,任何新的棋类游戏都有可能得到攻克。而在棋类游戏上的表现也可以直观体现出AI之间计算能力与算法的高低,是促进AI算法发展的有效途径。 就本五子棋智能对弈系统而言,其服务对象为同局域网下的多个终端,讲求联机互动、互相限制、互相博弈,打破了传统二人对弈五子棋规则中,“若无禁手,先手易胜;若为后手,十堵九输”的说法,是AI算法设计、网络通信、数据库等技术的综合应用。3 架构设计其中对弈数据库采用openGauss数据库4 数据表设计棋盘数据表的表头如图所示为将二维棋盘状态存入数据库中,令棋盘按 轴方向展开为一维序列玩家数据表的表头如图所示走子数据表的表头如图所示5 代码结构如图所示,依据模块化程序设计的基本思想,将整个项目按照功能划分为若干个小程序模块,每个小程序模块完成一个确定的功能,自顶向下、逐步分解、分而治之,各模块相对独立、功能单一、结构清晰。同时,在这些模块之间建立必要的联系,通过模块的互相协作完成整个功能的程序设计各模块具体设计如下:main.py:启动文件。config.py:变量管理文件。管理所有跨文件的全局变量。settings.py:配置文件。开发环境配置、第三方扩展插件参数配置、数据库的链接路径及其他配置等。apps:主体文件包。其初始化文件包括Flask类的实例创建以及工厂函数。该文件包下设三个子文件包,分别对应架构设计中的三张蓝图。exts:扩展文件包。第三方扩展插件的例化,创建映射对象等。 项目采用高度模块化设计的优点有:控制了程序设计的复杂性;提高了代码的重用性;易于维护和功能扩充;有利于团队开发等。6 项目演示7 总结基于openguass开发AI应用相当方便,也使我进一步掌握了数据库的常用技术,提高工程开发能力和面对未知问题的临场解决能力,对设计、创新、分析水平都有很大的帮助
  • [问题求助] cms报表算法及其理解求助
    问题来源】【必填】    海南农信【问题简要】【必填】     1、计算技能下面的等待时长(计算逻辑(排队+应答时长)/等待次数)对应在t_cms_callin_skill_day 表中哪些对应指标     2、t_cms_callin_skill_day 表中的succ_queue_time是不是指的是技能在队列中排队的时长,不包含应答时长吧【问题类别】【必填】   AICC cms 8.15.1【AICC解决方案版本】【必填】   AICC 8.15.1ICD V300R6c90u3spc700【期望解决时间】【选填】     尽快解决
  • [技术干货] 如何干涉MySQL优化器使用hash join?
    数据库的优化器相当于人类的大脑,大部分时候都能做出正确的决策,制定正确的执行计划,走出一条高效的路,但是它毕竟是基于某些固定的规则、算法来做的判断,有时候并没有我们人脑思维灵活,当我们确定优化器选择执行计划错误时该怎么办呢,语句上加hint,提示它选择哪条路是一种常见的优化方法。前言数据库的优化器相当于人类的大脑,大部分时候都能做出正确的决策,制定正确的执行计划,走出一条高效的路,但是它毕竟是基于某些固定的规则、算法来做的判断,有时候并没有我们人脑思维灵活,当我们确定优化器选择执行计划错误时该怎么办呢,语句上加hint,提示它选择哪条路是一种常见的优化方法。我们知道Oracle提供了比较灵活的hint提示来指示优化器在多表连接时选择哪种表连接方式,比如use_nl,no_use_nl控制是否使用Nest Loop Join,use_hash,no_use_hash控制是否使用hash join。但是MySQL长期以来只有一种表连接方式,那就是Nest Loop Join,直到MySQL8.0.18版本才出现了hash join, 所以MySQL在控制表连接方式上没有提供那么多丰富的hint给我们使用,hash_join与no_hash_join的hint只是惊鸿一瞥,只在8.0.18版本存在,8.0.19及后面的版本又将这个hint给废弃了,那如果我们想让两个表做hash join该怎么办呢?实验我们来以MySQL8.0.25的单机环境做一个实验。建两个表,分别插入10000行数据,使用主键做这两个表的关联查询。create table t1(id int primary key,c1 int,c2 int);create table t2(id int primary key,c1 int,c2 int);delimiter //CREATE PROCEDURE p_test()BEGINdeclare i int;set i=1;while i<10001 doinsert into t1 values(i,i,i);insert into t2 values(i,i,i);SET i = i + 1;end while;END;//delimiter ;查询一下两表使用主键字段关联查询时实际的执行计划,如下图所示:查询一下两表使用非索引字段关联查询时实际的执行计划,如下图所示:从执行计划可以看出,被驱动表的关联字段上有索引,优化器在选择表连接方式时会倾向于选择Nest Loop Join,当没有可用索引时倾向于选择hash join。基于这一点那我们可以使用no_index提示来禁止语句使用关联字段的索引。从上面的执行计划可以看出使用no_index提示后,优化器选择了使用hash join。当索引的选择性不好时,优化器选择使用索引做Nest Loop Join是效率是很低的。我们将实验的两个表中c1列的数据做一下更改,使其选择性变差,并在c1列上建普通索引。update t1 set c1=1 where id<5000;update t2 set c1=1 where id<5000;create index idx_t1 on t1(c1);create index idx_t2 on t2(c1);当我们执行sql :select t1.*,t2.* from t1 join t2 on t1.c1=t2.c1;这个查询结果会返回大量数据,被驱动表的关联字段c1列的索引选择性差,此时选择hash join是更明智的选择,但是优化器会选择走Nest Loop Join。我们可以通过实验验证一下hash join 与 Nest Loop Join的性能差异。可以看出使用hash join的耗时是使用Nest Loop Join的1/6,但是优化器根据成本估算时,使用Nest Loop Join的成本要比使用hash join的成本低很多,所以会去选择Nest Loop Join,这个时候就需要加上hint 提示禁止使用关联字段的索引,被驱动表上每次都全表扫描的代价是很高的,这样优化器估算后就会选择走hash join。MySQL官方文档里提到用BNL,NO_BNL的hint提示来影响hash join的优化,但是经过实验证明,在表连接关联字段上没有可用索引时,优化器估算成本后不会对被驱动表使用BNL全表扫描的方式做嵌套循环连接,而是会选择使用hash join,那这样NO_BNL在这个场景下就没有用武之地了。那么既然不用这个索引,把这个索引去掉不就可以了吗?为什么非要使用no_index的hint提示呢,我们要知道业务使用的场景何其多,此处不用,别处使用了这个索引效率可能会有大的提升啊,这个时候就凸显了hint的优势,只需要控制此语句的使用就好了。总结Nest Loop Join有其优势,它是response最快的连接方式,适用于返回数据量小的场景。当两个大表连接,返回大量数据,且关联字段的索引比较低效时,使用hash join就会比较高效,我们可以使用no_index的hint提示禁用关联字段的低效索引,促使优化器选择hash join。来源: GreatSQL社区
  • [技术干货] 解读《分布式数据库发展趋势研究报告》
    近期,由国家工业信息安全发展研究中心发布了2022年《分布式数据库发展趋势研究报告》。报告从数据库产业发展、分布式数据库产品价值、面临调整、技术路线、发展趋势、发展方向等多角度阐述了分布式数据库的诸多问题。本文,从个人角度谈谈对上述研究报告的解读。1、背景篇:不鸣则已、一鸣惊人伴随着数字化转型深化,企业对于数据的重视程度逐步加深。伴随着以云计算、5G、IOT、人工智能、区块链等新兴技术的发展,催着更多新兴数据场景的出现。这其中直观带来的就是数据规模呈现几何级增长、数据结构复杂度与日攀升。根据第三方机构的预测数据,全球数据存储量在未来几年将呈现爆炸式增长。除去数据规模外,在从数据采集、存储、传输、展现、分析和优化等方面都对数据的载体数据库提出了更高的要求。希望以此更好地实现企业对数据资产治理、增值与科学决策,数据高可靠高可用、数据在线分析等诉求,进一步发挥数据价值。上述诉求可以说为数据库提出了更高的要求,但传统数据库架构在超大规模、高并发、实时处理、数据安全等方面明显力不从心,此外高昂建设成本也难以迎合数字化时代的潮流趋势。相较于传统数据库,新兴分布式数据库的优势都凸显出来。突破规模化存储能力传统单机或集中式架构下,承载的数据规模受限于本地磁盘或可对接的外部存储空间。虽然后者可做到大规模(如PB级),但其建设周期长、扩展不灵活、投入成本高且依然还会面临IO的性能瓶颈。而分布式架构数据库,天然具有的数据分片能力,是有效解决超大规模数据承载的利器。突破高性能计算瓶颈作为承载计算的主要资源,CPU、内存资源对计算尤为重要。在传统单机或集中式架构下,上述资源仅能通过Scale UP方式进行扩展,其扩展能力有限,无法整合更多资源参与计算。而分布式架构数据库,通过网络可汇聚更多计算资源参与其中,形成更大规模的算力支持。在高并发、高性能计算领域更有优势。填补数据分析能力短板数据分析,是数据使用的重要方向之一,过去通常是由数据仓库等技术承载。这种在线数据库与离线数据仓库的架构,能在一定程度上解决数据分析问题,但在实时性、一致性、成本等方面存在短板。理想的方式在单一系统完成,但传统架构受限于资源,无法完全提供。而分布式架构数据库,通过其算力的整合可满足混合负载的业务压力,大幅度提升分析时效性,并减少数据冗余,灵活性大大提高。提升可用性与安全性传统架构下,数据库可用性更多取决于单点(或存储),设计上往往通过冗余硬件保护等方式去提升;但受限于架构约束难以达到非常高的可用性。针对数据安全,也通常是通过主备复制、备份等手段完成,但难以保证数据在线安全,需要窗口期完成恢复。分布式架构具备的存算分离、多副本、弹性扩展等能力,可有效提升整体可用性和数据安全。用户可根据需要,灵活调整架构,提升可用性和安全性。优化成本模型实现按需扩展传统架构数据库的成本相对高昂,这主要是由于其架构限制,向上扩展相对容易,水平扩展比较困难。为了保证快速的业务发展,通常在项目设计初期就需要按最大容量进行规划或为了更高的可靠性需要付出高额的成本。而分布式架构数据库则不同,其架构天然就支持灵活扩展能力(包括存储、计算),并可实现低成本的高可用解决方案(多副本)。上述能力,将有效降低企业在构建系统时的投入,特别是在面对快速、多变业务的场景更是如此。2、技术篇:百花齐放、百家争鸣从上图可见,分布式数据库已经发展多年,特别是近些年来已经逐步成熟并落地使用。从大的技术路线来看,可大致分为几种类型:路线:分布式中间件+单机数据库这一技术路线是在单机数据库系统上进行改造,主要解决计算存储的扩展性问题。上层为一组无状态计算节点,基于分片规则提供SQL解析,请求转发和结果合并的能力。下层为增强的单机数据库,提供单机数据库的存储和执行能力。这一架构通过数据在逻辑层的切割,可近似线性地对计算性能和存储容量进行扩展,具有可规模化扩展的能力。路线:分布式存储构建这一技术路线是通过构建分布式共享存储实现扩展,采用非对称计算节点,大部分公有云数据库是这条路线。这条路线有限地解决扩展性问题,跨地域数据一致性主要依赖分布式存储引擎。共享存储能够跨多个节点提供读写,上层的计算部分是无状态的一组节点组成。当有写能力的计算节点出现故障时,会自动从可用的读节点中自动选出一个作为写节点,实现写能力的高可用。路线:原生分布式这一技术路线是原生分布式数据库,各计算节点提供对等的读写服务。这条路线是根据分布式一致性协议做底层设计,与传统数据库有本质区别。原生分布式数据库将分布式存储、事务、计算有机的结合在一起,数据由系统自动打散并存储多个副本,通过一致性协议保证多个副本和事务日志的一致性,对分布式事务、全局MVCC等支持更为彻底。整个分布式结构是包裹在集群内部的,应用对此无感知。3、 趋势篇:石以砥焉、化钝为利随着分布式架构数据库在众多场景使用,再享受到其带来的收益之外,也对这一新架构产品提出了更多的挑战。这些挑战也为未来分布式数据库发展指明了方向。(1)融合化原生设计如上面谈到的分布式数据库存在不同路线,不同路线产品差异明显。从长期发展来看,不同路线产品呈现逐步融合的现象,各家各取所长,不断丰富产品能力。一方面相较于单机或集中式架构,分布式架构产品仍然存在诸多短板,可理解为基础能力补齐;另一方面用户对于分布式能力也提出了更高的要求,可理解为扩展能力增强。基础能力补齐​在分布式架构下,相较于单机或集中式架构,仍存在大量短板问题。这些会直接影响到用户的使用体验。如分布式事务的一致性保证问题,对于单机较容易实现的ACID,分布式环境中出现了更多的难题。分布式数据库将需要处理的事务进行拆分,再部署到不同的服务器上进行处理,理想状况下,整个过程需要全局一致性协议的保护,而分库分表两阶段的方式在一些意外情况下容易出现问题。再比如,分布式架构多采用存算分离架构,其天然会带来分层间网络开销问题,如何解决低延迟需求值得考虑。扩展能力增强​随着分布式数据库的使用,如何使用好这一架构成为核心。例如在分布式场景下,如何做好数据分片的智能化。分布式通过分库分表进行数据拆分,从而各表的数据量保持在阈值以下,从而应对高并发和海量数据,但如何高效的、高质量的进行分片,还需再探索。此外,作为以一种新架构产品,分布式数据库所具备的弹性扩缩容、按需扩展、海量支持、多副本细粒度控制等课题,都是值得深入挖掘。(2)负载一体化设计企业级应用的业务场景通常可以分为联机交易和实时分析两种,通常称为OLTP和OLAP的业务应用。由于是不同的应用场景,很多企业往往会选择多款数据库产品分别支持。这种组合式的解决方案要求数据在不同产品间进行流转,数据的同步过程就带来了时间延迟和数据不一致的风险,而且还会产生冗余数据,成本开销被迫提高,这在一定程度上限制了企业的发展。分布式数据库的出现,为企业解决上述问题带来了契机,这也是近年来HTAP(混合负载)的兴起,其旨在打破事务处理和分析之间“壁垒”。未来分布式数据库都应具备混合负载能力,即在支持高并发、事务性请求的同时,也对分析型的复杂查询提供了良好的支持,实现计算、I/O资源互不干扰。通过在线交易和分析互不影响,一站式地解决企业级应用的各种需求,从而大幅度降低成本,同时提高了企业决策的效率。(3)云与云原生设计根据全球知名咨询公司Gartner指出,“到2022年,75%的数据库将被部署或迁移到云平台…”云化无疑代表了未来。数据库作为IT基础设施,如何与云环境融合成为大家的问题。特别是分布式数据库,其架构需要大量资源构建。如何通过与云的结合,有效解决灵活部署、弹性扩缩容、资源管理乃至如何更好地利用云基础资源做到真正的云原生,这些都是分布式数据库需要考虑的。因此在分布式数据库产品设计层面就要充分适配云环境、兼容更多云技术,从而添加更多资源管控、多部署形态、云原生资源利用等云化方面的能力。(4)高可用一致性设计作为数据库的基本能力要求,服务高可用和数据一致性一直是企业选择数据库的重点考察要求。特别是随着数字化转型,更多数据参与到企业业务流转中,这些都对如可用性提出更好要求。传统数据库架构,在满足7×24小时的服务不中断和数据零丢失方面往往已经超出其可承受能力,或者即使能解决其成本也十分高昂。分布式数据库具备的分层、多组件、多节点架构成为解决高可用的基础,有效控制故障范围、主动发现自愈等手段可大幅提升服务可用性。同时,其多副本机制为数据一致性安全提供可能,这也是相较于传统架构的突破,可做到数据更为精细粒度的一致性,满足各种数据场景下对一致性的不同等级要求。(5)软硬结合异构设计硬件和软件是信息系统的核心组件,两者之间相辅相成,互相促进。新型硬件的出现,可以为数据库发展带来更多的收益。一方面以基础硬件如多核CPU、异构计算(如GPU、FPGA)持久化内存、高速网络为代表的硬件出现,为分布式数据库架构提供更多的想象空间;另一方面新型硬件也会为数据库设计带来更多挑战,如何利用好新硬件值得各数据库厂商思考,甚至会颠覆之前的设计模式。此外,作为关键基础设施,数据库还要为操作系统、芯片的灰度替换提供支持,而在关键行业及软件领域,数据库还需要提供对异构芯片的支持,从而提升数字化解决方案的严谨性,降低应用风险。(6)全密态安全设计当前信息安全已经上升到国家战略高度,诸多行业监管机构和政府部门对数据存储和使用都有明确的安全合规性要求。2021年11月公布的《中华人民共和国个人信息保护法》,监管部门已在金融等行业中推广数据加密,要求敏感数据采用加密的方式进行存储。而作为数据承载主体的数据库,有义务为此提供坚实的数据安全保障。那么在分布式数据库设计之初,就需考虑在数据传输、数据存储、数据计算等多方面的安全问题。诸如透明数据加密、透明数据传输、多密钥管理、国密算法支持、密态计算等方面,都需要考虑。(7)低成本集约化设计分布式数据库,作为一种新架构产品,对于企业来说会带来不小成本。从管理角度来看,分布式架构对运维人员都带来新的要求,如何管理好成为要点。产品是否提供完整的管理能力、是否提供完备的生态工具等,将直接影响最终使用成果。从资源角度来看,分布式架构需要一定资源投入,如何规划设计好并通过诸如租户能力有效降低使用成本很重要。(8)高兼容易迁移设计对于底层数据库替换,最为头疼的就是数据库的替换。大部分企业经过企业信息化的长期积累与革新,在内部积累了大量的业务系统。传统的企业级数据库产品提供了强大的能力,协助开发者快速便捷地构建应用程序,但同时也导致应用设计过度依赖数据库功能。适配新的数据库产品必须对应用代码进行大量修改。没有两个数据库是完全一样的,分布式数据库更是如此。其在底层架构、实现逻辑上必然存在差异。比较好的方式,就是提供高兼容能力,这将有利于大幅降低代码改造成本。目前大多数分布式数据库还不完全具备主流数据库生态的兼容能力,兼容的种类还不够丰富,兼容度还有待提高。此外,分布式架构对于设计上也有着特殊的要求,如何降低研发设计成本,近似透明地屏蔽这一差异很重要。此外,数据从传统集中式数据库迁移至分布式数据库是一项复杂且庞大的工程。从前期兼容评估、应用设计改造,到中期的业务测试、性能测试,指导最终迁移完成并保证迁移准确性等,这些都需要提供全流程的支持。这也是目前分布式产品普遍有所缺失的。希望未来分布式数据库产品将具备全方位、高标准、高可靠性的平滑迁移能力。4、发展篇:知之非艰、行之惟艰分布式数据库作为新技术架构,如何推进是需要多方位的支持。从近些年来看,从国家、行业、用户等多方位都给予大力支持。从政策层面,将在作为数据基础设施之一的数据库提升到一定高度,重点布局针对数据库分布式转型、应用创新战略。在行业方面,越来越多的数据库厂商加入进来,特别是以分布式为特征的产品已然成为主流。在用户方面,以金融、电信为代表的高数据价值企业,已经开始在核心生产系统逐步使用。但同时我们也看到,分布式数据库在推进中,仍然存在诸多不足之处,这也是未来需重点关注发展之处。(1)培育自有生态数据库要想用好,是需要从“产、学、研、用”多角度考虑,是需要跟上下游生态形成合力,才能为客户提供更为完整的服务。相较于之前国外商业数据库或开源产品,国内数据库生态还需要加大投入,培育自有生态。在这其中,可以通过生态兼容加速这一过程,如何有效利用之前成熟生态值得考虑。此外,开源作为一种有效的生态构建手段,也是生态化建设的利器。(2)共建行业标准作为一种新型数据库,分布式架构尚未形成统一的行业标准或者事实标准。从最终用户角度来看,不得不面对纷繁复杂的产品细节,这也阻碍了分布式数据库的大范围推广。从行业整体发展角度来看,一方面可通过行业指导单位牵头,由行业内众多企业参与形成标准规范;一方面可通过构建标准化评测体系,建立可衡量标准。希望通过标准的建立,能探索出特色发展路径,抓住机遇尽早实现分布式数据库领域的换道超车。(3)树立专有评测分布式数据库,作为新产品有其架构特色。对于这一新产品的理解,各家各有不同。行业内急需统一的评测标准,从多维度评估这一新类型产品。这其中既包括传统数据库的基本能力,也需要包含分布式自有特点,如在高可用、备份恢复等。逐步建立其以功能测试、非功能测试及场景化测试相结合,形成完备的评测体系。(4)填补最后路径企业更换底层数据库,是一个颇为痛苦的过程,更换为一种全新架构产品更是如此。大量用户使用分布式架构的担忧是来自于对新架构、新产品的未知及对实施路径的陌生。前者我们可通过专有评测标准逐步熟悉,后者则需要通过最后路径的填补做好“最后一公里”。从选型评估、工作量评估、结构数据迁移、流量切换、上线保障等多角度,形成标准的实施路径将大大加速这一过程。写在最后:分布式数据库,尚处于发展早期,但已呈现蓬勃之势。虽然仍有很多不足,但发展空间巨大。这里送给分布式数据库从业者一句话:道阻且长,行则将至;行而不辍,未来可期!作者介绍韩锋,51CTO社区编辑,CCIA(中国计算机协会)常务理事,前Oracle ACE,腾讯TVP,阿里云MVP,dbaplus等多家社群创始人或专家团成员。有着丰富的一线数据库架构、软件研发、产品设计、团队管理经验。曾担任多家公司首席DBA、数据库架构师等职。在云、电商、金融、互联网等行业均有涉猎,精通多种关系型数据库,对NoSQL及大数据相关技术也有涉足,实践经验丰富。曾著有数据库相关著作《SQL优化最佳实践》、《数据库高效优化》。来源:51CTO博客
  • [热门活动] 【我和openGauss的故事】第四届openGauss技术文章征集活动来啦!
    亲爱的伙伴们,Gauss松鼠会联合openGauss社区、鲲鹏社区、墨天轮共同共同举办【我和openGauss的故事】第四届openGauss技术文章征集活动踏秋而来~各位爱技术、爱思考、爱总结的小伙伴,你们施展才华的舞台已经搭建好~期待你的投稿作品!参与方式投稿:9月10日至10月15日,完成下面2步即投稿完成。在墨天轮社区或openGauss社区提交技术文章,并将链接发给Gauss松鼠会小助手(Gauss_Asst666)二维码:墨天轮社区:(cid:link_0),提交时需带有opengauss的标签。openGauss社区:https://opengauss.org   在墨天轮社区“我和openGauss的故事”有奖征文活动,将您发布的文章标题及链接复制黏贴到本活动宣传贴的评论区。参与评优:初审合格的文章将会同步发布在鲲鹏论坛-Database专区,我们会根据墨天轮社区&鲲鹏论坛的总点赞量进行片名,请关注文章的点赞量。活动推广:您还可以推荐好友参与投稿,每邀请2位好友完成openGauss社区投稿,可以获得“活动推广奖”。活动日程活动规则1、投稿:内容要求为openGauss相关技术文章,包含但不限于以下内容:系统技术解析、案例分享、实践总结、开发心得、客户案例、故障调试、测试比对、使用技巧、学习笔记等。文章要求原创且在墨天轮社区或openGauss社区首发,并且在墨天轮社区发布时需要加“openGauss”标签,在openGauss社区发布时需要加“openGauss技术文章征集”标签。若投稿文章为复制或抄袭他人文章,均视为无效。每篇文章要求不少于500字(可含代码),图文并茂,排版工整。2、初审:作者在投稿后,专家组将进行初审,通过初审的文章将参与评优活动;未通过初审的文章,专家组将给出修改建议,修改后可再次提交报名。3、评优通过初审的文章,将发布在鲲鹏论坛-DataBase专区,按墨天轮社区、鲲鹏论坛社区点赞的总赞数进行排名并给予奖励。奖品设置墨天轮投稿奖:根据文章价值和借鉴意义给与50~100元的激励。openGauss社区投稿奖:凡是在openGauss社区blog仓投稿并成功合入的,可以获得京东购物卡50/Gauss松鼠会保温杯/筋膜枪。​活动推广奖:(20个)每邀请2位好友参与活动,完成openGauss社区投稿,即可获得一份“活动推广奖”,多邀多得,数量有限,先到先得。优秀奖:通过初审的文章,将发布在鲲鹏论坛-DataBase专区,按墨天轮社区、鲲鹏论坛社区点赞的总赞数进行排名并给予奖励。其他奖项:在墨天轮社区发布的文章,同时还可以参加墨力计划,详细内容请参见:cid:link_2注:如参与者投稿多篇作品,仅按文章最好成绩参与优秀奖排名。墨天轮投稿奖,openGauss社区投稿奖,活动推广奖,优秀奖相互独立,每位参与活动者获奖可叠加。openGauss社区投稿奖、推广奖和优秀奖获奖者需在鲲鹏社区完成实名认证后方可领取奖品。优秀奖得主需在openGauss社区同步发布获奖文章后方可领取奖品。奖品种类数量有限,先到先得。奖励发放墨天轮投稿奖每满20篇,将在墨天轮编辑部账号上及时公布获奖名单,大家请多多关注!中奖用户可以联络墨天轮小助手(微信:modb666)领取墨天轮投稿奖。openGauss社区投稿奖、推广奖与优秀奖将于10月16日整理后发布,届时请联络Gauss松鼠会小助手领取。
  • [技术干货] 5分钟了解Redis的内部实现快速列表(quicklist)
    快速列表简介 在Redis3 .2版本之前,存储列表(list)数据结构使用的是压缩列表(ziplist)和链表(linkedlist),当列表元素个数比较少并且每个元素占用空间比较小的时候,使用压缩列表。当列表元素个数比较多或者某个元素占用空间比较大的时候,使用链表。  考虑到链表的附加空间相对太高,结点的内存也是单独分配的,影响内存管理效率。在Redis3 .2版本开始对列表数据结构进行了改造,使用快速列表(quicklist)代替了压缩列表(ziplist)和链表(linkedlist)。  快速列表(quicklist)是以压缩列表(ziplist)为节点的链表(linkedlist),将链表按段切分,每一段使用压缩列表进行内存的连续存储,多个压缩列表通过prev和next指针组成的双向链表。它结合了压缩列表和链表的优势,进一步压缩了内存的使用量,进一步提高了效率。  下面我们了解一下快速列表的具体实现。  快速列表的实现 在Redis中的快速列表是由quicklist结构表示的,quicklist结构包含由多个快速列表结点组成的双向链表,每一个快速列表结点都保存了一个压缩列表。下面我们一个一个地详细了解一下。  quicklist结构 快速列表是由quicklist结构表示的,它包含以下几个属性:  head属性: 指向头部快速列表结点的指针。 tail属性:指向尾部快速列表结点的指针。 count属性:在所有压缩列表中元素的个数总和。 len属性:快速列表结点的个数。 fill属性:压缩列表的最大大小,存放list-max-ziplist-size参数的值。当超出了这个配置,就会新建一个压缩列表。 compress属性:结点压缩深度,存放list-compress-depth参数的值。 bookmarks属性:用来快速列表重新分配内存空间时使用的数组,不使用时不占用空间。 bookmark_count属性:bookmarks数组的大小。 快速列表结点 快速列表结点使用quicklistNode结构表示,它包含以下几个属性:  prev属性:指向前一个快速列表结点的指针。 next属性:指向后一个快速列表结点的指针。 zl属性:指向压缩列表的指针,如果当前结点的数据被压缩,那么它指向一个quicklistLZF结构。 sz属性:压缩列表的所占字节总数。 count属性:压缩列表中的元素数量。 encoding属性:存储形式,原生字节数组还是LZF压缩存储。 recompress属性:当查看了某一项被压缩的数据时,需要把数据暂时解压,这时就设置 recompress = 1 做一个标记,等有机会再把数据重新压缩。 quicklistLZF结构 当快速列表结点数据被压缩时,数据会被存放在quicklistLZF结构中,它包含以下几个属性:  sz属性:表示压缩后的大小。 compressed属性:存放压缩后的字节数组。 快速列表的压缩机制 在快速列表中,两端结点的数据被访问的可能性比较高,中间结点的数据被访问的可能性比较低。如果我们的应用场景符合这个特点,可以把中间结点的数据使用 LZF 算法进行压缩,从而进一步节省内存空间。我们可以对list-compress-depth参数进行配置。  默认情况下,list-compress-depth参数为0,也就是不压缩数据;当该参数被设置为1时,除了头部和尾部之外的结点都会被压缩;当该参数被设置为2时,除了头部、头部的下一个、尾部、尾部的上一个之外的结点都会被压缩;当该参数被设置为2时,除了头部、头部的下一个、头部的下一个的下一个、尾部、尾部的上一个、尾部的上一个的上一个之外的结点都会被压缩;以此类推。 来源:51CTO博客
  • [技术干货] MySQL索引&事务
    写在前面 前面我们都是学习MySQL的操作,很少涉及到理论,有些sql语法前面我都没有谈,主要是工作中不常用,一般就是增删查改.要是实际工作中遇到了可以自己稍微查一下,都是很简单的.今天我们谈一下MySQL中被面试官常问的两个部分,都是理论知识,需要我们有自己的理解.  索引 我们先来解释一下什么是索引,这是我们的重点,它是属于MySQL数据库原理层面的知识,如果我们要是自己实现一个数据库,这里我们就要学习的很精通,要是我们岗位只是普通的程序猿,那么了解一下就可以了,至于如何用就不是我们现在这个层次考虑的了.  索引(index),就像是我们书的目录,我们根据目录可以快速的找到我们要看的章节,MySQL也是如此,索引在一定程度上可以加快我们查找数据的速率. 为何出现索引 我们到工作的时候就会发现,你修改数据的次数是远远低于查找数据的次数的,比如我们现在写的博客,一般而言,我写完之后,就很少更改它了,除非是有朋友指出这里存在巨大的错误,我一般都是再重温博客的时候看到有哪里不合适或者错误才会修改,一般都是用来复习和观看的. 同理MySQL也是如此.我们查看是很频繁的.这里就会出现一个问题,对于数据比较少的,我们查早还是很不错的,但是对于公司的服务器而言,这个数据可以实千万级的,那么这个时候我们还是按照老方法查找数据,那么一个命令就要等较长的时间.这时候我们就出现索引,至于索引的原理是什么,这里我们不讨论,知道到这里就可以了.  索引的缺点 前面我们只谈了索引可以提高效率,那么是索引难道就不存在缺点吗?我们想一下,书的目录是不是存在缺点,是的,最直观的一点就是废纸,同理索引是费空间.这就是索引最大的缺点. 随着我们数据量的增大,索引消耗的空间也会越来越大,这还是不是最关键的,对于书来说,我们每一次修改书的内容,那么目录随之也要进行校准,确保可以指定的位置是是准确的,同理MySQL也是如此.不过看起来索引有很大缺点,但是和优点相比较很微小的,瑕不掩瑜.我们在公司里面推荐用索引.  索引的使用 注意,这里的使用只是一点皮毛,甚至连皮毛都算不上,我们不学习使用索引.这里还要和大家谈一个东西,索引的创建最好在创表的时候就出现,要是你在数据比较的多的表来创建索引,那么有极大概率这个数据库会崩,所以要创就在开始的时候创建.  我们先来创建一个数据库,用来查看一下索引. create table student ( id int primary key, name varchar(50), score decimal(3,1));这里我们就可以查看这个数据表的索引了. -- 格式 show index from 表民;show index from student;这里我们就会疑惑了,我们好象是没有添加索引的那么这里为何会出现一个索引,准确来说,我们一个字段被主键或者唯一来约束,这一列就看做一个索引.我们的id就是一个索引.也就是说我们使用主键约束的时候还加快的查早的速率. 同理这里我们也可以给某一列添加索引. -- create index 索引名字 on 表名(列名);create index name_index on student(name);  同理这里我们也可以删除索引,这里只做简单的演示.注意这里容易把数据库给搞挂. drop index name_index on student;索引背后的数据结构 这里才是我们索引的重点,也是面试官比较喜欢问的.这里我们要好好的解释下.我们之前学了一点简单的数据结构,有顺序表,链表,二叉搜索树,哈希表等等,那么我们在想索引的底层是什么?  这里我们首先先排除三个,至于后面的二叉搜索树,可以不可以,这就是我们要讨论的了.  我们感觉二叉搜索树还是挺不错的,不过这里有个问题,我们好象查找数据的时候每一次都要比较,那么如果数据多了,树就高了,对于数据库每一次都意味着文件IO.这里还是不要太行.那么索引的底层究竟是什么?这里我们就要谈一个新的数据结构B+树.不过在谈这个树前,我们先来谈一下B树.注意,我们谈的数据库是MySQL,我这里只知道MySQL的索引是B+树,至于其他的是什么这里就不太清楚了.  B/B-树 我们先来解释一下这个名称,B树又叫B-树,记者B-树可不是念B建树,它是B树的另一个名称,从来没有什么B减树.这里算是解决一下我们的疑惑.  B树是一个N叉树,这个N叉比较特殊.对于树的每一个节点存在若干个数据把这个节点分为若干个区域.我们这里直接看树的的结构.  一个节点里面存在N个数据,把这个节点分为N+1个区域,每一个区域有指向一个新的节点,这就是B树.   这里我们简单的说一下B树的查找规则,这个和二叉搜索树是一样的,我们先来从根节点出发,根据比较来确定一个一个区域,这里逐渐寻找我们的数据.这里我们就疑惑了,这也是比较,而且比较的次数好象没有变少,那么这里就出现问题了,B树为何会提高索引的效率.这里由于我们还没有分享过文件IO,我先来说下,B树是不是高度变短了,这就就意味着以节点为基础比较变得少了,而磁盘IO也是根据节点的次数来计算的,所以这里提高效率了. B+树 B+树是在B树的基础上再次衍生出来的,基于索引而言,B+树是更加优秀的.我们现来看一下B+树的结构. B+树中每一个父节点的值会作为子节点的最大或者最小值,叶子节节点中会体现出来,而且对于叶子节点而言,我们使用指针把它给串联出来.   这里我们就要下一个结论了,B+树可以说是完美的给MySQL索引设计的,我们看一下它的优点. 树变短了,总体的IO次数变少了 所有的查询终究会落在叶子节点上,查询速度稳定 叶子节点通过链表链接出来后,很适合范围查找 所有的载荷都是放在叶子节点上的,非叶子节点只保存key值. 这里我先来解释一下最后一条,说人话就是我们把所有的数目只保留在在叶子节节点.这样我们的非叶子节点占据的空间很少,甚至可以在在内存中跑,这样也能大大减少磁盘IO,提高速率.  事务 上面总算是把索引谈的差不多了,这里还要接触这个知识点.事物还是一个比较好理解的知识点.我们先来看一下什么是事物.  事物,可以理解成打包,就是把几个工作一起做了,也就是要做都做,要不做都不做.  我们举一个例子,假设我要和自己的女朋哟去约会,首先第一点我要去ATM机中取钱,取完钱之后,我发现我女朋友鸽了我,这时候就是是一个很悲伤的故事.但是如果我们把这两个步骤打包成一个事物,也就是不会存在第一个步骤执行完了第二个步骤不会执行的情况. 原子性 那么我们就有问题了,事物是通过什么来保证的,这就要涉及到原子性了,这个算是线程里面的内容.我先来解释,在过去,人们认为原子是物质的最小单位,这里就用这个来命名了,没有其他的含义.我们再来举一个例子.假设存在一张账户表.   现在我们要做的就是A要给B转500元,就会执行下面的操作.假设第二步的时候出现了问题,也就是A的钱被扣了,但是B没有收到钱,至于造成这样的原因有很多种,比如服务器不小心断电了,数据库崩了等等.显然我们的原子性就是为何避免这种情况的发生.   如何保证事务 事务的保证就是下面的两条规则  要么都执行 要么都不执行 现在我们就疑惑了,我们该如何保证事务,要知道我们执行的结果成不成功是需要执行过之后才发现的,你这个规则好象把路给堵死了,这里的要不都不执行是需要带引号的,所谓的要不都不执行,是我们确实执行,如果成功了,万事大吉,错误了就把他给恢复回去,这种模式叫做回滚.至于如何恢复才是我们重点讨论的.还按照上面的例子来讨论,假设我们执行了第一个步骤,也就是A减去500,执行第二步出现了问题,导致无法执行,我们好象没有给B加上500.   这个时候数据库就会进行回滚,上一个步骤我们给A减去500,回滚的时候给A加上500,让它变回原来的样子.那么请问数据库是如何知道要给A加上500这个正确的操作的,这就又涉及到另外一个东西了.数据库会拿出一个小本本,把过去一段时间的操作记录下来,这就是我们传说中的日志.  事务的使用 我们好象还是没有谈过事务的使用,这里简单看一下就行了,也不是面试官主要的考点.  开启事务:start transaction; 2) 执行多条SQL语句 回滚或提交:rollback/commit 说明:rollback即是全部失败,commit即是全部成功. start transaction;-- 阿里巴巴账户减少2000update accout set mnotallow=money-2000 where name = '阿里巴巴';-- 四十大盗账户增加2000update accout set mnotallow=money+2000 where name = '四十大盗';commit; 事务的特性 面试官最喜欢问的问题就是事务的几个特性,这里我先总结下,后面还有好好谈谈.  原子性 一致性 持久性 隔离行 原子性前面我们已经分析过了,这里我先来谈一下一致性,事务一旦执行,执行的结果必须是合理合法的,也就是说余额不能出现为负数的情况.持久性也就是数据一旦正确存入,就会保存到硬盘中,被持久化存储起了.  隔离性 事务的隔离性在是在并发执行时体现的,并发是我们现在计算机常用的方法.  并发 这里我先来解释一下什么是并发执行.在我们使用计算机的时候,你会发现很多程序都在跑,不过CPU的个数远远要小于程序的个数,这个时候就会出现不够用的问题,并发在一定的程度上解决了这个问题,就是一般一个程序只占据CPU一段时间,然后换下一个.  脏读 这个是我们要重点谈的,可以说是它太重要的.我们先来假设一个场景,我的老师正在那里写代码,准备给我们布置作业,我偷偷的看了一眼,看到一个student类,那时候我就明白了,我们的作业和student类有关.我回去准备相关的知识了,到是在我走后,老师把题目给改了,这就是脏读问题.  不可以重复读 我吃了脏读的亏,这个时候我就比较小心了,我等到老师把代码写完,然后把他给上传到GitHub上,我在GitHub上读代码.这个模式算是老师写的时候我不能读,等到老师写完我读的代码就正确了.但是这里又出现了问题,我们没说毒的时候不能写啊,我在读第二遍的时候,老师把代码给改了.这个时候就是不可以重复的问题.   幻读 我们这个时候和老师在约定一下,我们读的时候你也不能写,这个在一定程度上解决了一不可重复读问题,但是这个真的很完美吗?这个时候老师的等的很无聊,既让我们不能修改原来的代码,那么我这里写另外一个代码总可以了吧.   这个时候又会出现另外一个问题,我们每一次刷新GitHub,有的代码一会有,一会没有,感觉是幻觉一样,这个就是幻读,算是一种特殊的不可重复读问题,要解决这个问题要彻底串行化执行.也就是老师写好了之后,可以直接去摸鱼了,我在这里观看就可以了. 来源:51CTO博客
  • [热门活动] 活动提醒!第二届【openGauss资料捉虫】活动,9月份精彩继续~
    为让更多的朋友了解、参与到openGauss开源社区建设中,并持续提升openGauss资料质量,Gauss松鼠会联合openGauss社区、鲲鹏社区、墨天轮共同开展第二届openGauss资料捉虫活动!报名参与openGauss资料捉虫活动,发现或解决资料中的待改进点,或者推荐他人参与活动,均有机会获奖。参与活动的伙伴不仅每月有机会获得华为无线耳机、华为手环等月度贡献奖,持续输出还有机会获得华为平板、机械键盘等优秀贡献奖。快来参与,让我们看到你的实力!参与方式01 活动报名活动时间:即日起至10月31日报名方式:添加“Gauss松鼠会小助手(ID:Gauss_Asst666)”为好友,发送“报名资料捉虫活动”以及Gitee用户名,即完成报名。02 参与活动方式一:直接参与社区贡献1.提交有效问题(Issue):在openGauss社区资料中寻找待改进的点,在docs仓库提交Issue反馈资料改进意见并在标题前加【openGauss资料捉虫活动】标签。操作指导请参见:cid:link_12.提交有效修改方法(PR,Pull Request) 在openGauss社区资料中寻找待改进的点或对于已存在未解决的Issue问题,在docs仓库按照提交PR修改资料并在标题前加【openGauss资料捉虫活动】标签。操作指导请参见:cid:link_2方式二:推荐他人参与社区贡献1.受邀者完成“openGauss资料捉虫活动”报名并发送邀请者微信昵称给Gauss松鼠会小助手。2.受邀者在openGauss社区提交有效问题(Issue)和修改方法(PR,Pull Request)。奖品设置参与奖提交1个有效的Issue和1个有效的PR,即可获赠华为官方出版的《openGauss数据库核心技术》、《openGauss数据库实战指南》、《openGauss数据库源码解析》书籍三选一,或Gauss松鼠会保温杯、华为三脚架自拍杆二选一。推广奖每邀请2个openGauss社区新用户,且受邀请人成功提交1个有效的Issue和1个有效的PR,邀请人即可获赠以下奖品之一。同一邀请人最多可获得5份礼品。月度贡献奖根据每月新增积分进行排名,定期在Gauss松鼠会公众号公布月度积分排行。贡献奖需要达到30积分以上才能获奖。如果月度参与人数大于50人,则一、二、三等奖品各增加1个,四等奖增加2个。优秀贡献奖根据累积积分进行排名,活动结束后(10月31日),根据总积分情况公布获奖名单。注:1.推广奖与月度贡献奖、优秀贡献奖相互独立,每位参与活动者获奖可叠加。     2.奖品种类数量有限,先到先得。活动规则本次捉虫活动采用积分制,包含如下2部分积分:1、邀请新用户参与活动:每成功邀请一个openGauss社区新用户(未参与过openGauss社区贡献),且受邀人提交1个有效Issue和1个有效的PR,邀请人即可获得20积分,积分依次累加。2、对有效的 Issue 和 PR 进行积分:项目积分规则Issue提交第一个Issue积3分,后续每提交一个Issue积1分。PR提交第一个PR积7分,后续每提交一个PR积2分。有效的Issue或PR的评定请参考:第二届openGauss资料捉虫活动宣传
总条数:323 到第
上滑加载中