学习clickhouse 集群搭建和分布式存储

news2024/12/28 18:10:49

为什么要用集群

使用集群的主要原因是为了提高系统的可扩展性、可用性和容错性。

  1. 可扩展性:当单个节点无法处理增加的负载时,可以通过添加更多的节点到集群来增加处理能力。这使得系统可以处理更大的数据量和更高的查询负载。
  2. 可用性:在集群中,如果一个节点出现故障,其他节点可以接管它的工作,从而保证系统的持续运行。这对于需要24/7运行的系统来说非常重要。
  3. 容错性:集群可以通过数据复制和故障转移来提高系统的容错性。如果一个节点出现故障,系统可以从其他节点获取数据,从而避免数据丢失。

为什么要分布式存储

使用分布式存储的主要原因有以下几点:

  1. 可扩展性:分布式存储系统可以通过添加更多的节点来扩展存储容量和处理能力,这使得它们可以处理大规模的数据。
  2. 高可用性:在分布式存储系统中,数据通常会在多个节点上进行复制,这意味着如果一个节点出现故障,其他节点可以提供数据,从而保证系统的可用性。
  3. 容错性:分布式存储系统通过数据复制和故障转移来提高系统的容错性。如果一个节点出现故障,系统可以从其他节点获取数据,从而避免数据丢失。
  4. 性能:分布式存储系统可以通过在多个节点上并行处理数据来提高性能。

那么今天我们今天用docker的形式搭建一个clickhouse集群来提供我们学习用。

dockerfile

docker-compose.yml

version: "3.7"
 
services:
  ck1:
    image: yandex/clickhouse-server
    ulimits:
      nofile:
        soft: 300001
        hard: 300002
    ports:
      - 9001:9000
    volumes:
      - ./conf/config.xml:/etc/clickhouse-server/config.xml
      - ./conf/users.xml:/etc/clickhouse-server/users.xml
      - ./conf/metrika1.xml:/etc/metrika.xml
    links:
      - "zk1"
    depends_on:
      - zk1
 
  ck2:
    image: yandex/clickhouse-server
    ulimits:
      nofile:
        soft: 300001
        hard: 300002
    volumes:
      - ./conf/metrika2.xml:/etc/metrika.xml
      - ./conf/config.xml:/etc/clickhouse-server/config.xml
      - ./conf/users.xml:/etc/clickhouse-server/users.xml
    ports:
      - 9002:9000
    depends_on:
      - zk1
 
  ck3:
    image: yandex/clickhouse-server
    ulimits:
      nofile:
        soft: 300001
        hard: 300002
    volumes:
      - ./conf/metrika3.xml:/etc/metrika.xml
      - ./conf/config.xml:/etc/clickhouse-server/config.xml
      - ./conf/users.xml:/etc/clickhouse-server/users.xml
    ports:
      - 9003:9000
    depends_on:
      - zk1
 
  zk1:
    image: zookeeper:3.5
    restart: always
    hostname: zk1
    expose:
      - "2181"
    ports:
      - 2181:2181

注意:这里clickhouse的版本要和zookeeper的版本相互匹配上,不然后续就会有兼容性的问题。我的clickhouse的版本是22.1.3 所以我的zookeeper 的版本是3.5

配置

  1. user.xml
<?xml version="1.0"?>
<clickhouse>
    <!-- Profiles of settings. -->
    <profiles>
        <!-- Default settings. -->
        <default>
            <!-- Maximum memory usage for processing single query, in bytes. -->
            <max_memory_usage>10000000000</max_memory_usage>


            <!-- How to choose between replicas during distributed query processing.
                 random - choose random replica from set of replicas with minimum number of errors
                 nearest_hostname - from set of replicas with minimum number of errors, choose replica
                  with minimum number of different symbols between replica's hostname and local hostname
                  (Hamming distance).
                 in_order - first live replica is chosen in specified order.
                 first_or_random - if first replica one has higher number of errors, pick a random one from replicas with minimum number of errors.
            -->
            <load_balancing>random</load_balancing>
            <allow_ddl>1</allow_ddl>
            <readonly>0</readonly>
        </default>

        <!-- Profile that allows only read queries. -->
        <readonly>
            <readonly>1</readonly>
        </readonly>
    </profiles>

    <!-- Users and ACL. -->
    <users>
        <!-- If user name was not specified, 'default' user is used. -->
        <default>
                <access_management>1</access_management>
            <password></password>

            <networks>
                <ip>::/0</ip>
            </networks>

            <!-- Settings profile for user. -->
            <profile>default</profile>

            <!-- Quota for user. -->
            <quota>default</quota>

            <!-- User can create other users and grant rights to them. -->
            <!-- <access_management>1</access_management> -->
        </default>
        <test>
            <password></password>
            <quota>default</quota>
            <profile>default</profile>
            <allow_databases>
                <database>default</database>
                <database>test_dictionaries</database></allow_databases>
            <allow_dictionaries>
                <dictionary>replicaTest_all</dictionary>
            </allow_dictionaries>
        </test>
    </users>

    <!-- Quotas. -->
    <quotas>
        <!-- Name of quota. -->
        <default>
            <!-- Limits for time interval. You could specify many intervals with different limits. -->
            <interval>
                <!-- Length of interval. -->
                <duration>3600</duration>

                <!-- No limits. Just calculate resource usage for time interval. -->
                <queries>0</queries>
                <errors>0</errors>
                <result_rows>0</result_rows>
                <read_rows>0</read_rows>
                <execution_time>0</execution_time>
            </interval>
        </default>
    </quotas>
</clickhouse>
  1. config.xml
<?xml version="1.0"?>
<!--
  NOTE: User and query level settings are set up in "users.xml" file.
  If you have accidentally specified user-level settings here, server won't start.
  You can either move the settings to the right place inside "users.xml" file
   or add <skip_check_for_incorrect_settings>1</skip_check_for_incorrect_settings> here.
-->
<clickhouse>
    <logger>
        <level>trace</level>
        <log>/var/log/clickhouse-server/clickhouse-server.log</log>
        <errorlog>/var/log/clickhouse-server/clickhouse-server.err.log</errorlog>
        <size>1000M</size>
        <count>10</count>
    </logger>

    <http_port>8123</http_port>
    <tcp_port>9000</tcp_port>
    <mysql_port>9004</mysql_port>
    <postgresql_port>9005</postgresql_port>
    <interserver_http_port>9009</interserver_http_port>
    <max_connections>4096</max_connections>
    <keep_alive_timeout>3</keep_alive_timeout>
    <grpc>
        <enable_ssl>false</enable_ssl>
        <ssl_cert_file>/path/to/ssl_cert_file</ssl_cert_file>
        <ssl_key_file>/path/to/ssl_key_file</ssl_key_file>
        <ssl_require_client_auth>false</ssl_require_client_auth>
        <ssl_ca_cert_file>/path/to/ssl_ca_cert_file</ssl_ca_cert_file>
        <compression>deflate</compression>
        <compression_level>medium</compression_level>
        <max_send_message_size>-1</max_send_message_size>
        <max_receive_message_size>-1</max_receive_message_size>
        <verbose_logs>false</verbose_logs>
    </grpc>
    <openSSL>
        <server>
            <certificateFile>/etc/clickhouse-server/server.crt</certificateFile>
            <privateKeyFile>/etc/clickhouse-server/server.key</privateKeyFile>
            <dhParamsFile>/etc/clickhouse-server/dhparam.pem</dhParamsFile>
            <verificationMode>none</verificationMode>
            <loadDefaultCAFile>true</loadDefaultCAFile>
            <cacheSessions>true</cacheSessions>
            <disableProtocols>sslv2,sslv3</disableProtocols>
            <preferServerCiphers>true</preferServerCiphers>
        </server>

        <client> 
            <loadDefaultCAFile>true</loadDefaultCAFile>
            <cacheSessions>true</cacheSessions>
            <disableProtocols>sslv2,sslv3</disableProtocols>
            <preferServerCiphers>true</preferServerCiphers>
            <invalidCertificateHandler>
                <name>RejectCertificateHandler</name>
            </invalidCertificateHandler>
        </client>
    </openSSL>
    <max_concurrent_queries>100</max_concurrent_queries>
    <max_server_memory_usage>0</max_server_memory_usage>
    <max_thread_pool_size>10000</max_thread_pool_size>
    <max_server_memory_usage_to_ram_ratio>0.9</max_server_memory_usage_to_ram_ratio>
    <total_memory_profiler_step>4194304</total_memory_profiler_step>

    <total_memory_tracker_sample_probability>0</total_memory_tracker_sample_probability>
    <uncompressed_cache_size>8589934592</uncompressed_cache_size>
    <mark_cache_size>5368709120</mark_cache_size>
    <mmap_cache_size>1000</mmap_cache_size>
    <compiled_expression_cache_size>134217728</compiled_expression_cache_size>
    <compiled_expression_cache_elements_size>10000</compiled_expression_cache_elements_size>
    <path>/var/lib/clickhouse/</path>
    <tmp_path>/var/lib/clickhouse/tmp/</tmp_path>
    <user_files_path>/var/lib/clickhouse/user_files/</user_files_path>
    <ldap_servers>
    </ldap_servers>
    <user_directories>
        <users_xml>
            <path>users.xml</path>
        </users_xml>
        <local_directory>
            <path>/var/lib/clickhouse/access/</path>
        </local_directory>
    </user_directories>
    <default_profile>default</default_profile>
    <custom_settings_prefixes></custom_settings_prefixes>
    <default_database>default</default_database>
    <mlock_executable>true</mlock_executable>
    <remap_executable>false</remap_executable>

    <![CDATA[
         Uncomment below in order to use JDBC table engine and function.

         To install and run JDBC bridge in background:
         * [Debian/Ubuntu]
           export MVN_URL=https://repo1.maven.org/maven2/ru/yandex/clickhouse/clickhouse-jdbc-bridge
           export PKG_VER=$(curl -sL $MVN_URL/maven-metadata.xml | grep '<release>' | sed -e 's|.*>\(.*\)<.*|\1|')
           wget https://github.com/ClickHouse/clickhouse-jdbc-bridge/releases/download/v$PKG_VER/clickhouse-jdbc-bridge_$PKG_VER-1_all.deb
           apt install --no-install-recommends -f ./clickhouse-jdbc-bridge_$PKG_VER-1_all.deb
           clickhouse-jdbc-bridge &

         * [CentOS/RHEL]
           export MVN_URL=https://repo1.maven.org/maven2/ru/yandex/clickhouse/clickhouse-jdbc-bridge
           export PKG_VER=$(curl -sL $MVN_URL/maven-metadata.xml | grep '<release>' | sed -e 's|.*>\(.*\)<.*|\1|')
           wget https://github.com/ClickHouse/clickhouse-jdbc-bridge/releases/download/v$PKG_VER/clickhouse-jdbc-bridge-$PKG_VER-1.noarch.rpm
           yum localinstall -y clickhouse-jdbc-bridge-$PKG_VER-1.noarch.rpm
           clickhouse-jdbc-bridge &

         Please refer to https://github.com/ClickHouse/clickhouse-jdbc-bridge#usage for more information.
    ]]>
    <remote_servers incl="clickhouse_remote_servers">
        <test_unavailable_shard>
            <shard>
                <replica>
                    <host>localhost</host>
                    <port>9000</port>
                </replica>
            </shard>
            <shard>
                <replica>
                    <host>localhost</host>
                    <port>1</port>
                </replica>
            </shard>
        </test_unavailable_shard>
    </remote_servers>

    <zookeeper incl="zookeeper-servers">
    </zookeeper>
    <builtin_dictionaries_reload_interval>3600</builtin_dictionaries_reload_interval>
    <max_session_timeout>3600</max_session_timeout>
    <default_session_timeout>60</default_session_timeout>
    <query_log>
        <database>system</database>
        <table>query_log</table>
        <partition_by>toYYYYMM(event_date)</partition_by>
        <flush_interval_milliseconds>7500</flush_interval_milliseconds>
    </query_log>
    <trace_log>
        <database>system</database>
        <table>trace_log</table>

        <partition_by>toYYYYMM(event_date)</partition_by>
        <flush_interval_milliseconds>7500</flush_interval_milliseconds>
    </trace_log>
    <query_thread_log>
        <database>system</database>
        <table>query_thread_log</table>
        <partition_by>toYYYYMM(event_date)</partition_by>
        <flush_interval_milliseconds>7500</flush_interval_milliseconds>
    </query_thread_log>
    <query_views_log>
        <database>system</database>
        <table>query_views_log</table>
        <partition_by>toYYYYMM(event_date)</partition_by>
        <flush_interval_milliseconds>7500</flush_interval_milliseconds>
    </query_views_log>
    <part_log>
        <database>system</database>
        <table>part_log</table>
        <partition_by>toYYYYMM(event_date)</partition_by>
        <flush_interval_milliseconds>7500</flush_interval_milliseconds>
    </part_log>
    <metric_log>
        <database>system</database>
        <table>metric_log</table>
        <flush_interval_milliseconds>7500</flush_interval_milliseconds>
        <collect_interval_milliseconds>1000</collect_interval_milliseconds>
    </metric_log>
    <asynchronous_metric_log>
        <database>system</database>
        <table>asynchronous_metric_log</table>
        <flush_interval_milliseconds>7000</flush_interval_milliseconds>
    </asynchronous_metric_log>
    <opentelemetry_span_log>
        <engine>
            engine MergeTree
            partition by toYYYYMM(finish_date)
            order by (finish_date, finish_time_us, trace_id)
        </engine>
        <database>system</database>
        <table>opentelemetry_span_log</table>
        <flush_interval_milliseconds>7500</flush_interval_milliseconds>
    </opentelemetry_span_log>
    <crash_log>
        <database>system</database>
        <table>crash_log</table>

        <partition_by />
        <flush_interval_milliseconds>1000</flush_interval_milliseconds>
    </crash_log>

    <session_log>
        <database>system</database>
        <table>session_log</table>

        <partition_by>toYYYYMM(event_date)</partition_by>
        <flush_interval_milliseconds>7500</flush_interval_milliseconds>
    </session_log>
    <top_level_domains_lists>
    </top_level_domains_lists>
    <dictionaries_config>*_dictionary.xml</dictionaries_config>
<user_defined_executable_functions_config>*_function.xml</user_defined_executable_functions_config>
    <encryption_codecs>
       
    </encryption_codecs>

    <distributed_ddl>
        <path>/clickhouse/task_queue/ddl</path>
    </distributed_ddl>
    <graphite_rollup_example>
        <pattern>
            <regexp>click_cost</regexp>
            <function>any</function>
            <retention>
                <age>0</age>
                <precision>3600</precision>
            </retention>
            <retention>
                <age>86400</age>
                <precision>60</precision>
            </retention>
        </pattern>
        <default>
            <function>max</function>
            <retention>
                <age>0</age>
                <precision>60</precision>
            </retention>
            <retention>
                <age>3600</age>
                <precision>300</precision>
            </retention>
            <retention>
                <age>86400</age>
                <precision>3600</precision>
            </retention>
        </default>
    </graphite_rollup_example>

    <format_schema_path>/var/lib/clickhouse/format_schemas/</format_schema_path>
    <query_masking_rules>
        <rule>
            <name>hide encrypt/decrypt arguments</name>
            <regexp>((?:aes_)?(?:encrypt|decrypt)(?:_mysql)?)\s*\(\s*(?:'(?:\\'|.)+'|.*?)\s*\)</regexp>
            <replace>\1(???)</replace>
        </rule>
    </query_masking_rules>



    <send_crash_reports>
        <enabled>false</enabled>
        <anonymize>false</anonymize>
        <endpoint>https://6f33034cfe684dd7a3ab9875e57b1c8d@o388870.ingest.sentry.io/5226277</endpoint>
    </send_crash_reports>
    <include_from>/etc/metrika.xml</include_from>
</clickhouse>
  1. metrika.xml
    这里我们3个metrika.xml都是一样的
<yandex>
  <clickhouse_remote_servers>
    <test_cluster>
      <shard>
        <internal_replication>true</internal_replication>
        <replica>
          <host>ck1</host>
          <port>9000</port>
        </replica>
      </shard>
      <shard>
        <internal_replication>true</internal_replication>
        <replica>
          <host>ck2</host>
          <port>9000</port>
        </replica>
      </shard>
      <shard>
        <internal_replication>true</internal_replication>
        <replica>
          <host>ck3</host>
          <port>9000</port>
        </replica>
      </shard>
    </test_cluster>
  </clickhouse_remote_servers>

  <zookeeper-servers>
    <node index="1">
      <host>zk1</host>
      <port>2181</port>
    </node>
  </zookeeper-servers>

</yandex>

我们将配置文件在conf文件夹里面创建好以后执行docker-compose up -d 就可以启动我们的集群了。

创建表

通过docker-compose ps 查看端口号
在这里插入图片描述
通过clickhouse-client --port 9001 连接数据库
在这里插入图片描述

创建数据表

分表在3台节点啥


CREATE TABLE access_logs
(
    `create_time` DateTime,
    `status` String,
    `runtime` String,
    `client_ip` String,
    `method` String,
    `url` String,
    `body` String,
    `response` String,
    `hander` String,
    `request_id` String
)
ENGINE = ReplacingMergeTree
PARTITION BY toYYYYMM(create_time)
PRIMARY KEY request_id
ORDER BY (request_id, create_time)
TTL create_time + toIntervalHour(48)

找一台节点执行一下sql

CREATE TABLE access_logs_all ENGINE = Distributed(test_cluster, big_data_all, access_logs, rand());

第二条sql的意思,我们通过on cluster在每个节点上创建了一个分布式的表,该表指向了我们已经有的表access_logs。

test_cluster : 集群的名称
big_data_all: 远程数据库的名称
access_logs: 远程数据表的名称
rand(): 分片的逻辑,我们这里使用的是随机分配,还有hivehash 等。

Distributed Engine

ClickHouse 的 Distributed 引擎并不存储数据,而是允许在物理上分布在多个节点上的表之间进行查询。当一个查询被发送到 Distributed 表时,它将查询分发到所有的本地表,然后汇总结果。

  1. 当一个查询被发送到 Distributed 表时,ClickHouse 首先将查询分解为两部分:一个主查询和一个子查询。

  2. 子查询被发送到集群中的每个节点。每个节点在其本地表上执行子查询,并返回结果。

  3. 主查询在 Distributed 表所在的节点上执行。它从所有的子查询中收集结果,然后汇总这些结果。

  4. 如果查询包含聚合函数,如 SUM()COUNT(),那么 ClickHouse 将在每个节点上进行部分聚合,然后在 Distributed 表所在的节点上进行最终聚合。

  5. 如果查询包含 ORDER BY 子句,那么排序将在每个节点上进行,然后在 Distributed 表所在的节点上进行最终排序。

通过这种方式,Distributed 引擎可以在多个节点上并行执行查询,从而提高查询性能。同时,由于数据是在多个节点上存储的,因此可以提供更大的存储容量。

在 ClickHouse 中,你可以直接向 Distributed 表插入数据。当你向 Distributed 表插入数据时,ClickHouse 会根据分片键将数据分发到相应的分片上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1498352.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java面试篇【并发编程】常见面试题(2024最新)

Java并发编程常见面试题 1.什么是线程和进程&#xff1f; 进程是操作系统分配资源的最小单位&#xff0c;各个进程之间占据独立的寻址空间&#xff0c;运行也是独立运行&#xff0c;进程间通信需要一些机制。进程间切换需要的开销较大。 线程是程序执行的基本单位&#xff0c…

力扣经典题目解析--删除链表的倒数第 N 个结点

原题地址:. - 力扣&#xff08;LeetCode&#xff09; 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;h…

计算机网络 八股

计算机网络体系结构 OSI&#xff1a;物理层、数据链路层、网络层、运输层、会话层、表示层、应用层

Leetcoder Day38| 动态规划part05 背包问题

1049.最后一块石头的重量II 有一堆石头&#xff0c;每块石头的重量都是正整数。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结果如下&#xff1a; 如果 x y&#xff0c;那…

稀碎从零算法笔记Day11-LeetCode:有效的字母异位词

题型&#xff1a;字符串、哈希表、排序 链接&#xff1a;242. 有效的字母异位词 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 注意&#xff1a;若 s 和 …

opengl 学习(一)-----创建窗口

创建窗口 分类opengl 学习(一)-----创建窗口效果解析教程补充 分类 c opengl opengl 学习(一)-----创建窗口 demo: #include "glad/glad.h" #include "glfw3.h" #include <iostream> #include <cmath> #include <vector>using names…

.Net6使用JWT认证和授权

文章目录 目的实现案例一.项目所需包&#xff1a;二.配置项目 appsettings.json 文件&#xff1a;三.创建Model文件夹&#xff0c;添加AppConfig类和UserRole类1.AppConfig类获取appsettings.json文件中的值2.UserRole类用于区分用户信息和权限 四.主体代码案例&#xff1a;1.L…

Javaweb之Web后端开发总结的详细解析

4. Web后端开发总结 到此基于SpringBoot进行web后端开发的相关知识我们已经学习完毕了。下面我们一起针对这段web课程做一个总结。 我们来回顾一下关于web后端开发&#xff0c;我们都学习了哪些内容&#xff0c;以及每一块知识&#xff0c;具体是属于哪个框架的。 web后端开…

想用Python做自动化测试?Python反射机制的应用!

通常&#xff0c;我们操作对象的属性或者方法时&#xff0c;是通过点“.”操作符进行的。例如下面的代码&#xff1a; class Person: type "mammal"def __init__(self, name): self.name namedef say_hi(self): print(Hello, my name is, self.n…

vue页面刷新问题:返回之前打开的页面,走了create方法(解决)

vue页面刷新问题&#xff1a;返回之前打开的页面&#xff0c;走了create方法&#xff08;解决&#xff09; 直接上图&#xff0c; 我们在开发的时候经常会复制粘贴&#xff0c;导致vue文件的name没有及时修改 我们需要保证name和浏览器的地址一致&#xff0c;这样才能实现缓…

面试题个人总结(面经)

自我介绍 你好&#xff0c;我叫XXX&#xff0c;是今天面试初级蓝队的人员&#xff0c;我毕业于XXXX&#xff0c;专业为网络空间安全,我曾经在XXXXX实习过&#xff0c;有过大概一年左右的工作经验&#xff0c;还有过一定的护网经验&#xff0c;去年在XXX厂商护过网&#xff0c;…

大型网站架构演化总结

本文图解大型网站架构演化。 目录 1、单一应用服务阶段 2、应用与数据服务分离阶段 3、利用缓存提高性能阶段 4、应用服务集群阶段 5、数据库读写分离阶段 6、反向代理与CDN加速阶段 7、分布式数据库阶段 8、 NoSQL与搜索引擎阶段 9、业务拆分阶段 10、分布式服务阶…

电脑要用多少V的电源?电脑电源输入电压是市电

台式电源的输出电压是多少&#xff1f; 电脑电源输出一般有三种不同的电压&#xff0c;分别是&#xff1a; 12V、5V、3.3V。 电脑电源负责给电脑配件供电&#xff0c;如CPU、主板、内存条、硬盘、显卡等&#xff0c;是电脑的重要组成部分。 工作电流根据不同的硬件及其使用状…

从仓储管理看3C电子行业智慧物流的优势

仓储管理是智慧物流的重要组成部分。通过引入自动化、智能化的仓储管理系统&#xff0c;3C电子企业可以实现库存的精准管理、快速分拣和高效配送。这不仅减少了库存成本&#xff0c;还大大提高了运营效率和市场响应速度。 传统的仓储管理依赖于人工操作和纸质文档记录&#xff…

Java项目:37 springboot003图书个性化推荐系统的设计与实现

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 springboot003图书个性化推荐系统的设计与实现 管理员&#xff1a;首页、个人中心、学生管理、图书分类管理、图书信息管理、图书预约管理、退换图书…

unity学习(50)——服务器三次注册限制以及数据库化角色信息5--角色信息数据库化收尾

上一节内容结束后确实可以写入文件了&#xff0c;但还有两个问题&#xff1a; 1.一个是players.txt中&#xff0c;每次重启服务器&#xff0c;当注册新账号创建角色时&#xff0c;players.txt之前内容都会清空。 2.players.txt之前已经注册3次的账号&#xff0c;新注册的角色…

做接口测试的流程一般是怎么样的?2024全网最全教程(建议收藏)

在讲接口流程测试之前&#xff0c;首先需要给大家申明下&#xff1a;接口测试对于测试人员而言&#xff0c;非常非常重要&#xff0c;懂功能测试接口测试&#xff0c;就能在企业中拿到一份非常不错的薪资。不信请狠狠点击下方链接&#xff08;下方链接也有接口学习方法及学习资…

Vue中的v-for中为什么不推荐使用index作为key值

在Vue中&#xff0c;我们经常会用到v-for指令来遍历数组或对象并渲染列表。而在使用v-for指令时&#xff0c;通常会需要给每个遍历的元素指定一个唯一的key值&#xff0c;以帮助Vue更高效地更新DOM。 在很多情况下&#xff0c;我们可能会倾向于使用index作为key值&#xff0c;…

Qt程序设计-柱状温度计自定义控件实例

Qt程序设计-柱状温度计自定义控件实例 本文讲解Qt柱状温度计自定义控件实例。 效果演示 创建温度计类 #ifndef THERMOMETER_H #define THERMOMETER_H#include <QWidget> #include <QPainter> #include <QDebug> #include <QTimer> #include <QPr…

Unity之街机捕鱼

目录 &#x1f62a;炮台系统 &#x1f3b6;炮口方向跟随鼠标 &#x1f3b6;切换炮台 &#x1f62a;战斗系统 &#x1f3ae;概述 &#x1f3ae;单例模式 &#x1f3ae;开炮 &#x1f3ae;子弹脚本 &#x1f3ae;渔网脚本 &#x1f3ae;鱼属性信息的脚本 &#x1f6…