postgresql 10.5 主从复制--搭建测试
env:
role | master | slave |
host | pg1 | pg2 |
ip | 11 | 12 |
pg-version | 10.5 | 10.5 |
1 初始化查看
[ceiec@localhost ~]$ df -h
[ceiec@localhost ~]$ free -m
[ceiec@localhost ~]$ lscpu
[ceiec@localhost ~]$ cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
--master
[root@localhost ~]# hostnamectl set-hostname pg1
--slave
[root@localhost ~]# hostnamectl set-hostname pg2
官方下载源码包
https://www.postgresql.org/ftp/source/
2 解压并安装
# tar zxvf postgresql-10.5.tar.gz
# cd postgresql-10.5/
# mkdir /usr/local/postgresql
# yum install -y zlib-devel readline-devel gcc
# ./configure --prefix=/usr/local/postgresql --with-python --with-perl
编译时增加 --with-python
configure: error: header file <Python.h> is required for Python
解决方法:
yum install python-devel
编译时增加 --with-perl
configure: error: could not determine flags for linking embedded Perl.
This probably means that ExtUtils::Embed or ExtUtils::MakeMaker is notinstalled.
解决方法:
yum install perl-ExtUtils-Embed
用户、路径、权限
# useradd postgres
# mkdir -p /home/pgdata
# chown -R postgres.postgres /home/pgdata
# vim /etc/profile
export PATH=/usr/local/postgresql/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/postgresql/lib
# source /etc/profile
# vim /etc/hosts
*.11 pg1
*.12 pg2
# chown -R postgres:postgres /usr/local/postgresql
postgres用户profile
[root@localhost postgresql]# su - postgres
[postgres@pg1 ~]$ vim /home/postgres/.bash_profile
export PGHOME=/usr/local/postgresql
export PGDATA=/home/pgdata
export PGHOST=/tmp
export PATH="$HOME/bin:$HOME/.local/bin:$PATH:$PGHOME/bin"
export MANPATH=$PGHOME/share/man:$MANPATH
export LANG=en_US.utf8
export DATE=`date +"%Y-%m-%d %H:%M:%S"`
export LD_LIBRARY_PATH=$PGHOME/lib:$LD_LIBRARY_PATH
[postgres@pg1 ~]$ source .bash_profile
初始化数据库
$ /usr/local/postgresql/bin/initdb -D /home/pgdata
修改参数文件
[postgres@pg1 pgdata]$ vim postgresql.conf
listen_addresses = '*'
max_connections = 1000
shared_buffer = 10240MB
wal_level = hot_standby
synchronous_commit = on
checkpoint_timeout = 5min
archive_mode = on
archive_command = '/bin/date'
max_wal_senders = 10
wal_keep_segments = 16
hot_standby = on
max_standby_archive_delay = 30s
max_standby_streaming_delay = 30s
wal_receiver_status_interval = 1s
hot_standby_feedback = on
wal_receiver_timeout = 60s
连接权限
[postgres@pg1 pgdata]$ vim pg_hba.conf
host all all *.7.0/24 md5
host all all *.12/32 md5
host all all *.0/32 md5
host all all *.11/32 md5
host replication repuser *.12/32 md5
启动
$ pg_ctl start -D /home/pgdata/
[postgres@pg1 pgdata]$ netstat -lnt|grep 5432
tcp 0 0 0.0.0.0:5432 0.0.0.0:* LISTEN
tcp6 0 0 :::5432 :::* LISTEN
[postgres@pg1 pgdata]$ psql
psql (10.5)
Type "help" for help. postgres=# create role repuser login replication encrypted password '***';
CREATE ROLE
postgres=# \q
--salve配置 ,在初始化db前的操作都一样,slave上不需要初始化
从库安装完成后,不初始化,若已经初始化,删除其data目录
[postgres@pg2 ~]$ pg_basebackup -D /home/pgdata -F p -X stream -R -v -P -h *.11 -p 5432 -U repuser
Password:
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/2000028 on timeline 1
pg_basebackup: starting background WAL receiver
23740/23740 kB (100%), 1/1 tablespace
pg_basebackup: write-ahead log end point: 0/20000F8
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: base backup completed
配置备库参数postgresql.conf
[postgres@pg2 pgdata]$ vim postgresql.conf
#在基础备份时,初始化文件是从主库复制来的,所以配置文件一致,注释掉
wal_level,
max_wal_senders
wal_keep_segments等参数
打开如下参数:
hot_standby = on #在备份的同时允许查询
max_standby_streaming_delay = 30s #可选,流复制最大延迟
wal_receiver_status_interval = 10s #可选,从向主报告状态的最大间隔时间
hot_standby_feedback = on #可选,查询冲突时向主反馈
max_connections = 1100 #默认参数,非主从配置相关参数,表示到数据库的连接数,一般从库做主要的读服务时,设置值需要高于主库
[postgres@pg2 pgdata]$ vim pg_hba.conf
host replication repuser *.11/32 md5
[postgres@pg2 data]# cat recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=repuser password=****** host=*.11 port=5432 sslmode=prefer sslcompression=1 krbsrvname=postgres'
[postgres@pg2 pgdata]$ vim recovery.conf
recovery_target_timeline = 'latest'
启动
[postgres@pg2 pgdata]$ pg_ctl start
waiting for server to start....2019-07-20 03:00:00.026 CST [19694] FATAL: data directory "/home/pgdata" has group or world access
2019-07-20 03:00:00.026 CST [19694] DETAIL: Permissions should be u=rwx (0700).
stopped waiting
pg_ctl: could not start server
Examine the log output.
[postgres@pg2 pgdata]$ chmod 700 /home/pgdata
[postgres@pg2 pgdata]$ pg_ctl start
进程查看
--master
[postgres@pg1 pgdata]$ ps -ef|grep postgres
root 1363 10030 0 09:59 pts/0 00:00:00 su - postgres
postgres 1364 1363 0 09:59 pts/0 00:00:00 -bash
postgres 6608 11510 0 10:59 ? 00:00:00 postgres: wal sender process repuser *.12(50726) streaming 0/3000140
postgres 7486 1364 0 11:00 pts/0 00:00:00 ps -ef
postgres 7487 1364 0 11:00 pts/0 00:00:00 grep --color=auto postgres
postgres 11510 1 0 10:18 pts/0 00:00:00 /usr/local/postgresql/bin/postgres -D /home/pgdata
postgres 11512 11510 0 10:18 ? 00:00:00 postgres: checkpointer process
postgres 11513 11510 0 10:18 ? 00:00:00 postgres: writer process
postgres 11514 11510 0 10:18 ? 00:00:00 postgres: wal writer process
postgres 11515 11510 0 10:18 ? 00:00:00 postgres: autovacuum launcher process
postgres 11516 11510 0 10:18 ? 00:00:00 postgres: archiver process last was 000000010000000000000002.00000028.backup
postgres 11517 11510 0 10:18 ? 00:00:00 postgres: stats collector process
postgres 11518 11510 0 10:18 ? 00:00:00 postgres: bgworker: logical replication launcher
--slave
[postgres@pg2 pgdata]$ ps -ef|grep postgres
root 17802 4245 0 02:30 pts/0 00:00:00 su - postgres
postgres 17803 17802 0 02:30 pts/0 00:00:00 -bash
postgres 19891 1 0 03:02 pts/0 00:00:00 /usr/local/postgresql/bin/postgres
postgres 19892 19891 0 03:02 ? 00:00:00 postgres: startup process recovering 000000010000000000000003
postgres 19893 19891 0 03:02 ? 00:00:00 postgres: checkpointer process
postgres 19894 19891 0 03:02 ? 00:00:00 postgres: writer process
postgres 19895 19891 0 03:02 ? 00:00:00 postgres: stats collector process
postgres 19896 19891 0 03:02 ? 00:00:00 postgres: wal receiver process streaming 0/3000140
postgres 19914 17803 0 03:02 pts/0 00:00:00 ps -ef
postgres 19915 17803 0 03:02 pts/0 00:00:00 grep --color=auto postgres
状态查看
--master
postgres=# select client_addr,sync_state from pg_stat_replication;
client_addr | sync_state
--------------+------------
*.12 | async
(1 row) postgres=# select * from pg_stat_replication;
pid | usesysid | usename | application_name | client_addr | client_hostname | client_port | backend_start | backend_xmin | state | sent_ls
n | write_lsn | flush_lsn | replay_lsn | write_lag | flush_lag | replay_lag | sync_priority | sync_state
------+----------+---------+------------------+--------------+-----------------+-------------+-------------------------------+--------------+-----------+--------
---+-----------+-----------+------------+-----------+-----------+------------+---------------+------------
6608 | 16384 | repuser | walreceiver | *.12 | | 50726 | 2019-07-19 10:59:51.168278+08 | 556 | streaming | 0/30001
40 | 0/3000140 | 0/3000140 | 0/3000140 | | | | 0 | async
(1 row) postgres=# create user yhq with password '****';
CREATE ROLE
postgres=# create database test1 owner yhq;
CREATE DATABASE
postgres=# grant all privileges on database test1 to yhq;
GRANT
postgres=# \c test1
You are now connected to database "test1" as user "postgres".
test1=# CREATE TABLE user_tbl(name VARCHAR(20), signup_date DATE);
CREATE TABLE
test1=# INSERT INTO user_tbl(name, signup_date) VALUES('yhqtest','2019-07-19');
INSERT 0 1
test1=# select *from user_tbl;
name | signup_date
---------+-------------
yhqtest | 2019-07-19
(1 row)
第一次主从切换
--master
--master
[postgres@pg1 pgdata]$ pg_controldata | grep 'Database cluster state'
Database cluster state: in production
[postgres@pg1 pgdata]$ psql
psql (10.5)
Type "help" for help. postgres=# \c test1
You are now connected to database "test1" as user "postgres".
test1=# CREATE TABLE t1(id integer not null,date TIMESTAMP NOT NULL DEFAULT LOCALTIMESTAMP(0));
CREATE TABLE
test1=# INSERT INTO t1 (id) VALUES (1);
INSERT 0 1
test1=# INSERT INTO t1 (id) VALUES (2);
INSERT 0 1
test1=# INSERT INTO t1 (id) VALUES (3);
INSERT 0 1
test1=# select * from t1;
id | date
----+---------------------
1 | 2019-07-19 14:26:27
2 | 2019-07-19 14:26:30
3 | 2019-07-19 14:26:32
(3 rows)
--slave
--slave
[postgres@pg2 pgdata]$ pg_controldata | grep 'Database cluster state'
Database cluster state: in archive recovery
[postgres@pg2 pgdata]$ psql
psql (10.5)
Type "help" for help. postgres=# \c test1
You are now connected to database "test1" as user "postgres".
test1=# select * from t1;
id | date
----+---------------------
1 | 2019-07-19 14:26:27
2 | 2019-07-19 14:26:30
3 | 2019-07-19 14:26:32
(3 rows)
test1=# INSERT INTO t1 (id) VALUES (4);
2019-07-20 06:33:51.823 CST [] ERROR: cannot execute INSERT in a read-only transaction
2019-07-20 06:33:51.823 CST [] STATEMENT: INSERT INTO t1 (id) VALUES (4);
ERROR: cannot execute INSERT in a read-only transaction
--master关掉pg
--master
[postgres@pg1 pgdata]$ pg_ctl stop -m fast
waiting for server to shut down....2019-07-19 14:37:47.168 CST [] LOG: received fast shutdown request
2019-07-19 14:37:47.171 CST [] LOG: aborting any active transactions
2019-07-19 14:37:47.172 CST [] FATAL: terminating connection due to administrator command
2019-07-19 14:37:47.172 CST [] LOG: worker process: logical replication launcher (PID 7905) exited with exit code 1
2019-07-19 14:37:47.174 CST [] LOG: shutting down
Fri Jul 19 14:37:47 CST 2019
2019-07-19 14:37:47.484 CST [] LOG: database system is shut down
done
server stopped
[postgres@pg1 pgdata]$ pg_controldata | grep 'Database cluster state'
Database cluster state: shut down
--slave
--slave
--错误日志
2019-07-20 06:41:10.304 CST [] FATAL: could not connect to the primary server: could not connect to server: Connection refused
Is the server running on host "*.11" and accepting
TCP/IP connections on port 5432? [postgres@pg2 pgdata]$ pg_ctl promote
waiting for server to promote....2019-07-20 06:41:13.568 CST [] LOG: received promote request
2019-07-20 06:41:13.568 CST [] LOG: redo done at 0/5000028
2019-07-20 06:41:13.568 CST [] LOG: last completed transaction was at log time 2019-07-19 14:26:32.495757+08
2019-07-20 06:41:13.573 CST [] LOG: selected new timeline ID: 2
2019-07-20 06:41:14.116 CST [] LOG: archive recovery complete
2019-07-20 06:41:14.130 CST [] LOG: database system is ready to accept connections
Sat Jul 20 06:41:14 CST 2019
Sat Jul 20 06:41:14 CST 2019
Sat Jul 20 06:41:14 CST 2019
done
server promoted
[postgres@pg2 pgdata]$ pg_controldata | grep 'Database cluster state'
Database cluster state: in production
[postgres@pg2 pgdata]$ psql
psql (10.5)
Type "help" for help. postgres=# \c test1
You are now connected to database "test1" as user "postgres".
test1=# INSERT INTO t1 (id) VALUES (4);
INSERT 0 1
test1=# INSERT INTO t1 (id) VALUES (5);
INSERT 0 1
test1=# INSERT INTO t1 (id) VALUES (6);
INSERT 0 1
test1=# INSERT INTO t1 (id) VALUES (7);
INSERT 0 1
test1=# select * from t1;
id | date
----+---------------------
1 | 2019-07-19 14:26:27
2 | 2019-07-19 14:26:30
3 | 2019-07-19 14:26:32
4 | 2019-07-20 06:42:29
5 | 2019-07-20 06:42:36
6 | 2019-07-20 06:42:42
7 | 2019-07-20 06:42:44
(7 rows)
[postgres@pg2 pgdata]$ ll recovery.done
-rw-rw-r--. 1 postgres postgres 199 Jul 20 02:58 recovery.done
--旧master修改并启动且加入
--master
[postgres@pg1 pgdata]$ ll recovery
ls: cannot access recovery: No such file or directory
[postgres@pg1 pgdata]$ vim recovery.conf
recovery_target_timeline = 'latest'
standby_mode = 'on'
primary_conninfo = 'user=repuser password=***** host=*.12 port=5432 sslmode=disable sslcompression=1 target_session_attrs=any'
[postgres@pg1 pgdata]$ vim postgresql.conf
max_connections = 1200
#max_wal_senders
#wal_keep_segments
[postgres@pg1 pgdata]$ pg_ctl start
[postgres@pg1 pgdata]$ pg_controldata | grep 'Database cluster state'
Database cluster state: in archive recovery
[postgres@pg1 pgdata]$ psql
psql (10.5)
Type "help" for help. postgres=# \c test1
You are now connected to database "test1" as user "postgres".
test1=# select * from t1;
id | date
----+---------------------
1 | 2019-07-19 14:26:27
2 | 2019-07-19 14:26:30
3 | 2019-07-19 14:26:32
4 | 2019-07-20 06:42:29
5 | 2019-07-20 06:42:36
6 | 2019-07-20 06:42:42
7 | 2019-07-20 06:42:44
(7 rows)
--再次把新slave停掉
[postgres@pg2 pgdata]$ pg_ctl stop -m fast
--旧master作为主
[postgres@pg1 pgdata]$ pg_ctl promote
[postgres@pg1 pgdata]$ pg_controldata | grep 'Database cluster state'
Database cluster state: in production
[postgres@pg1 pgdata]$ psql
psql (10.5)
Type "help" for help. postgres=# \c test1
You are now connected to database "test1" as user "postgres".
test1=# INSERT INTO t1 (id) VALUES (17);
INSERT 0 1
test1=# INSERT INTO t1 (id) VALUES (18);
INSERT 0 1
test1=# INSERT INTO t1 (id) VALUES (19);
INSERT 0 1
test1=# select * from t1;
id | date
----+---------------------
1 | 2019-07-19 14:26:27
2 | 2019-07-19 14:26:30
3 | 2019-07-19 14:26:32
4 | 2019-07-20 06:42:29
5 | 2019-07-20 06:42:36
6 | 2019-07-20 06:42:42
7 | 2019-07-20 06:42:44
17 | 2019-07-19 14:48:22
18 | 2019-07-19 14:48:27
19 | 2019-07-19 14:48:30
(10 rows)
--最开始的slave,依然作为slave加入
--slave
[postgres@pg2 pgdata]$ mv recovery.done recovery.conf
[postgres@pg2 pgdata]$ pg_ctl start
--new master
--master
test1=# select client_addr,sync_state from pg_stat_replication;
client_addr | sync_state
--------------+------------
*.12 | async
(1 row)
--slave
[postgres@pg2 pgdata]$ psql
psql (10.5)
Type "help" for help. postgres=# \c test1
You are now connected to database "test1" as user "postgres".
test1=# select * from t1;
id | date
----+---------------------
1 | 2019-07-19 14:26:27
2 | 2019-07-19 14:26:30
3 | 2019-07-19 14:26:32
4 | 2019-07-20 06:42:29
5 | 2019-07-20 06:42:36
6 | 2019-07-20 06:42:42
7 | 2019-07-20 06:42:44
17 | 2019-07-19 14:48:22
18 | 2019-07-19 14:48:27
19 | 2019-07-19 14:48:30
(10 rows)
--note:
1 pg1和pg2的时间不一样,暴露了在初始化的时候没有修改正确
同步2台主机的时间
[root@pg2 ~]# date
Sat Jul 20 06:56:13 CST 2019
[root@pg1 pgdata]# date
Fri Jul 19 14:48:55 CST 2019 [root@pg1 pgdata]# ntpdate 0.arch.pool.ntp.org
19 Jul 15:00:41 ntpdate[]: step time server *** offset 155.499032 sec
[root@pg2 ~]# ntpdate 0.arch.pool.ntp.org
19 Jul 14:59:55 ntpdate[]: adjust time server *** offset 0.004792 sec
2 sync_state=async 异步流复制
修改为sync同步参数
#synchronous_standby_names = '' # standby servers that provide sync rep
# method to choose sync standbys, number of sync standbys,
# and comma-separated list of application_name
# from standby(s); '*' = all
postgres=# SELECT client_addr,application_name,sync_state FROM pg_stat_replication;
client_addr | application_name | sync_state
--------------+------------------+------------
*.12 | walreceiver | async
(1 row)
[postgres@pg1 pgdata]$ vim postgresql.conf
[postgres@pg1 pgdata]$ pg_ctl reload
server signaled
2019-07-19 15:44:09.263 CST [] LOG: received SIGHUP, reloading configuration files
2019-07-19 15:44:09.264 CST [] LOG: parameter "synchronous_standby_names" changed to "walreceiver"
2019-07-19 15:44:14.879 CST [] LOG: standby "walreceiver" is now a synchronous standby with priority 1 postgres=# SELECT client_addr,application_name,sync_state FROM pg_stat_replication;
client_addr | application_name | sync_state
--------------+------------------+------------
*.12 | walreceiver | sync
(1 row)
postgres=# \x
Expanded display is on.
postgres=# select * from pg_stat_replication;
-[ RECORD 1 ]----+------------------------------
pid | 3985
usesysid | 16384
usename | repuser
application_name | walreceiver
client_addr | *.12
client_hostname |
client_port | 50758
backend_start | 2019-07-19 14:51:11.747452+08
backend_xmin | 573
state | streaming
sent_lsn | 0/60005E8
write_lsn | 0/60005E8
flush_lsn | 0/60005E8
replay_lsn | 0/60005E8
write_lag |
flush_lag |
replay_lag |
sync_priority | 1
sync_state | sync
postgresql 10.5 主从复制--搭建测试的更多相关文章
- PostgreSQL 10首个测试版本发布
mysql 从5.7到8.0,pg从9.6到10,干起来了.. PostgreSQL 10 的首个测试版发布了,此版本包含 PostgreSQL 10 最终将提供的所有功能的预览.当然,有些细节将在最 ...
- redis实现主从复制-单机测试
一.redis实现主从复制-单机测试1.安装redis tar -zxvf redis-2.8.4.tar.gzcd redis-2.8.4make && make install2. ...
- MYSQL主从复制搭建及切换操作(GTID与传统)
结构如下: MYSQL主从复制方式有默认的复制方式异步复制,5.5版本之后半同步复制,5.6版本之后新增GTID复制,包括5.7版本的多源复制. MYSQL版本:5.7.20 操作系统版本:linux ...
- MySQLDocker 主从复制搭建
MySQLDocker 主从复制搭建 MySQLDocker 的搭建 docker search mysql docker pull mysql/mysql-server:8.0.26 docker ...
- PostgreSQL 10.7 linux 主从配置
PostgreSQL 10.7 主从安装 硬件环境 云服务商:华为云 Linux: CentOS7.1 工具:Xshell Xftp IP:114.115.251.168 Port: 5432 543 ...
- 大数据基础-2-Hadoop-1环境搭建测试
Hadoop环境搭建测试 1 安装软件 1.1 规划目录 /opt [root@host2 ~]# cd /opt [root@host2 opt]# mkdir java [root@host2 o ...
- Nginx配置多个基于域名的虚拟主机+实验环境搭建+测试
标签:Linux 域名 Nginx 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://xpleaf.blog.51cto.com/9 ...
- Mybatis框架一:搭建测试
Mybatis框架不再介绍: 在JDBC中存在一些问题: 1.频繁连接和释放资源浪费内存 2.编码完成后不便于维护 于是产生了简化数据库操作的框架:Hibernate.Mybatis等等,这里介绍My ...
- 02.基于IDEA+Spring+Maven搭建测试项目--详细过程
一.背景介绍 1.1公司相关技术 Git:是一款免费的开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目,方便多人集成开发 Maven:是基于项目对象模型(POM),可以通过一小段描述信息 ...
随机推荐
- 安卓的几种alert对话框
@Override public void onClick(View v) { switch (v.getId()) { case R.id.d1: AlertDialog.Builder build ...
- php 中英文混合字符串长度计算
(strlen($string) + mb_strlen($string,'UTF8')) / 2;tw 这样计算的
- 微软宣布全新命令行+脚本工具:PowerShell 7
DOS 逐渐退出历史舞台后,Windows 一直内置着 CMD 命令行工具,并在 Windows 7 时代升级为更强悍的 PowerShell,不仅可以执行命令行,更可以执行各种高级脚本,还能跨平台. ...
- 【视频点播最佳实践】使用OSS SDK上传视频到点播
摘要: 场景 点播上传SDK缺乏需要的语言版本(如C/C++.Go等)或相应的功能(如网络流上传.追加上传),可以直接使用OSS的SDK进行上传. 准备工作 确认已开通点播服务并完成了相关配置.确认已 ...
- cm日志哪里看
root@d001:/home/centos# find / |grep cloudera-scm-agent.log/opt/cm-5.13.0/log/cloudera-scm-agent/clo ...
- 【NOIP2016提高A组模拟9.24】我的快乐时代
题目 分析 虽然我们很难求出\(\sum_{i=n}^mjoy(i)\), 但是我们可以分别求出\(\sum_{i=1}^mjoy(i)\)和\(\sum_{i=1}^{n-1}joy(i)\),相减 ...
- 关于python pip安装第三方库 jieba 中文分词工具后提示"ImportError: cannot import name 'Random'"报错问题
具体错误提示如下: >>> import jieba Traceback (most recent call last): File "<stdin>" ...
- 查看SSL证书的别名
1.把java目录下的keytool拷贝到证书目录下:2.进入证书目录,然后输入命令keytool -list -v -keystore file.jks -storepass password,发现 ...
- 大文件的分片传,断点续传,md5校验
一.概述 所谓断点续传,其实只是指下载,也就是要从文件已经下载的地方开始继续下载.在以前版本的HTTP协议是不支持断点的,HTTP/1.1开始就支持了.一般断点下载时才用到Range和Content- ...
- BZOJ 3294: [Cqoi2011]放棋子 计数 + 容斥 + 组合
比较头疼的计数题. 我们发现,放置一个棋子会使得该棋子所在的1个行和1个列都只能放同种棋子. 定义状态 $f_{i,j,k}$ 表示目前已使用了 $i$ 个行,$j$ 个列,并放置了前 $k$ 种棋子 ...