จะวางฐานข้อมูล PostgreSQL ได้อย่างไรหากมีการเชื่อมต่อที่ใช้งานอยู่


648

ฉันต้องเขียนสคริปต์ที่จะทิ้งฐานข้อมูล PostgreSQL อาจมีการเชื่อมต่อกับมันมากมาย แต่สคริปต์ควรละเว้นสิ่งนั้น

DROP DATABASE db_nameแบบสอบถามมาตรฐานไม่ทำงานเมื่อมีการเชื่อมต่อที่เปิดอยู่

ฉันจะแก้ปัญหาได้อย่างไร


1
คุณกำลังใช้ PostgreSQL รุ่นใดอยู่
Kuberchaun

1
ปัญหา: ในขณะที่คุณอาจฆ่าเซสชันที่เชื่อมต่อกับฐานข้อมูลพวกเขาอาจเชื่อมต่อใหม่อย่างรวดเร็วจนคุณยังไม่สามารถวางฐานข้อมูลได้ มีความสุขที่โพสต์นี้แสดงให้เห็นถึงวิธีการล็อคการเชื่อมต่อใหม่ดังนั้นคุณสามารถฆ่าการเชื่อมต่อปัจจุบันและวางฐานข้อมูลตามแผน: dba.stackexchange.com/questions/11893/…
Max Murphy

1
ฉันพบคำตอบนี้ใน dba.stackexchange ว่ามีประโยชน์มากdba.stackexchange.com/a/11895/163539 - รวบรัด แต่มีคำอธิบายที่เพียงพอ
hlongmore

คำตอบ:


1093

การดำเนินการนี้จะลดการเชื่อมต่อที่มีอยู่ยกเว้นสำหรับคุณ

ค้นหาpg_stat_activityและรับค่า pid ที่คุณต้องการฆ่าแล้วออกค่าSELECT pg_terminate_backend(pid int)เหล่านั้น

PostgreSQL 9.2 ขึ้นไป:

SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE pg_stat_activity.datname = 'TARGET_DB' -- ← change this to your DB
  AND pid <> pg_backend_pid();

PostgreSQL 9.1 และต่ำกว่า:

SELECT pg_terminate_backend(pg_stat_activity.procpid)
FROM pg_stat_activity
WHERE pg_stat_activity.datname = 'TARGET_DB' -- ← change this to your DB
  AND procpid <> pg_backend_pid();

เมื่อคุณยกเลิกการเชื่อมต่อทุกคนคุณจะต้องตัดการเชื่อมต่อและออกคำสั่ง DATP DATABASE จากการเชื่อมต่อจากฐานข้อมูลอื่นหรือที่ไม่ใช่ที่คุณพยายามจะวาง

หมายเหตุ: การเปลี่ยนชื่อของคอลัมน์procpid pidดูกระทู้รายการจดหมายนี้


11
และแน่นอนว่าต้องทำเช่นนั้นจากการเชื่อมต่อฐานข้อมูลที่ไม่ใช่การเชื่อมต่อกับ 'TARGET_DB' มิฉะนั้นคุณจะได้รับ 'ข้อผิดพลาด' การเชื่อมต่อ 'postgres' ทำงานได้ดี
Rob

3
ที่จริงแล้วมันจะตัดการเชื่อมต่อลูกค้าทีละคนและหากคุณลูกค้าอยู่ตรงกลางของรายการมันจะถูกยกเลิกการเชื่อมต่อด้วย เป็นผลให้การเชื่อมต่อบางอย่างจะมีชีวิตอยู่ ดังนั้นคำตอบที่ถูกต้องคือโดย Craig Ringer (ดูด้านล่าง) เลือก pg_terminate_backend (pg_stat_activity.pid) จาก pg_stat_activity WHERE datname = current_database () และ pg_stat_activity.pid <> pg_backend_pid ();
Andrew Selivanov

1
ฉันจะยกเลิกการเชื่อมต่อการเชื่อมต่อหลังจากที่พวกเขาทำธุรกรรมปัจจุบันเสร็จสิ้นแล้วจากนั้นปล่อยตารางที่เป็นปัญหาได้
paulkon

5
ในกรณีลูกค้าของฉันจะเชื่อมต่อใหม่อย่างรวดเร็วดังนั้นจึงควรดำเนินการนี้ก่อนที่จะ; drop database TARGET_DB;ทำงานได้ดีในกรณีของฉันเพื่อให้แน่ใจว่าฐานข้อมูลหายไปเมื่อเวลาที่เริ่มลองใหม่
Mat Schaffer

1
dropdb --forceฉันก็จะจ่ายเงินสำหรับ
Torsten Bronger

125

ใน PostgreSQL 9.2 ขึ้นไปเพื่อยกเลิกการเชื่อมต่อทุกอย่างยกเว้นเซสชันของคุณจากฐานข้อมูลที่คุณเชื่อมต่อ:

SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE datname = current_database()
  AND pid <> pg_backend_pid();

ในรุ่นเก่าก็เหมือนกันการเปลี่ยนแปลงเพียงเพื่อpid procpidหากต้องการยกเลิกการเชื่อมต่อจากฐานข้อมูลอื่นcurrent_database()ให้เปลี่ยนเป็นชื่อของฐานข้อมูลที่คุณต้องการยกเลิกการเชื่อมต่อกับผู้ใช้

คุณอาจต้องการที่จะที่เหมาะสมจากผู้ใช้ฐานข้อมูลผู้ใช้ก่อนที่จะถอดมิฉะนั้นผู้ใช้ก็จะเก็บไว้ในการเชื่อมต่อใหม่และคุณจะไม่ได้รับโอกาสที่จะลดลงฐานข้อมูล ดูความคิดเห็นนี้และคำถามที่เกี่ยวข้องกับฉันจะแยกผู้ใช้รายอื่นออกจากฐานข้อมูลได้อย่างไรREVOKECONNECT

หากคุณต้องการยกเลิกการเชื่อมต่อผู้ใช้ที่ไม่ได้ใช้งานให้ดูคำถามนี้


3
เลือก pg_terminate_backend (pg_stat_activity.pid) จาก pg_stat_activity WHERE datname = current_database () และ pg_stat_activity.pid <> pg_backend_pid ();
Andrew Selivanov

26

คุณสามารถฆ่าการเชื่อมต่อทั้งหมดก่อนที่จะทิ้งฐานข้อมูลโดยใช้pg_terminate_backend(int)ฟังก์ชั่น

คุณสามารถเรียกใช้แบ็กเอนด์ที่ทำงานอยู่ทั้งหมดโดยใช้มุมมองระบบ pg_stat_activity

ฉันไม่แน่ใจทั้งหมด แต่ต่อไปนี้อาจฆ่าเซสชันทั้งหมด:

select pg_terminate_backend(procpid)
from pg_stat_activity
where datname = 'doomed_database'

แน่นอนคุณอาจไม่ได้เชื่อมต่อตัวเองกับฐานข้อมูลนั้น


19

ขึ้นอยู่กับรุ่น postgresql ของคุณคุณอาจพบข้อผิดพลาดซึ่งทำให้pg_stat_activityละเว้นการเชื่อมต่อที่ใช้งานอยู่จากผู้ใช้ที่ถูกทิ้ง การเชื่อมต่อเหล่านี้จะไม่ปรากฏใน pgAdminIII

หากคุณทำการทดสอบอัตโนมัติ (ซึ่งคุณสร้างผู้ใช้ด้วย) นี่อาจเป็นสถานการณ์ที่น่าจะเป็น

ในกรณีนี้คุณต้องเปลี่ยนกลับเป็นคำค้นหาเช่น:

 SELECT pg_terminate_backend(procpid) 
 FROM pg_stat_get_activity(NULL::integer) 
 WHERE datid=(SELECT oid from pg_database where datname = 'your_database');

หมายเหตุ: ใน 9.2+ คุณจะมีการเปลี่ยนแปลงไปprocpidpid


1
นั่นคือสิ่งที่ฉันกำลังมองหา แต่สำหรับ (สมมติว่า 9.2 ขึ้นไป) คุณต้องลบการอ้างอิงถึง pg_stat_activity และเปลี่ยน procpid เป็น pid
MDR

2
หลังจากเปลี่ยนprocpidเป็นpidข้อมูลโค้ดนี้ทำงานบน 9.3
jb

แม้ไม่ลบ pg_stat_activity? ฉันได้รับข้อผิดพลาดเมื่อ 9.2
MDR

ตกลง. ตอนนี้ฉันเข้าใจแล้วนั่นเป็นคำที่สะกดผิด ขอบคุณ!
jb

2
จาก 9.3 และสูงกว่า SELECT pg_terminate_backend (pid) จาก pg_stat_get_activity (NULL :: จำนวนเต็ม) WHERE datid = (SELECT oid จาก pg_database โดยที่ datname = 'your_database');
Shawn Vader

17

ฉันสังเกตเห็นว่า postgres 9.2 ตอนนี้เรียกว่าคอลัมน์ pid แทนที่จะเป็น procpid

ฉันมักจะเรียกมันจากเปลือก:

#!/usr/bin/env bash
# kill all connections to the postgres server
if [ -n "$1" ] ; then
  where="where pg_stat_activity.datname = '$1'"
  echo "killing all connections to database '$1'"
else
  echo "killing all connections to database"
fi

cat <<-EOF | psql -U postgres -d postgres 
SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
${where}
EOF

หวังว่าจะเป็นประโยชน์ ขอบคุณ @JustBob สำหรับ sql


15

ฉันเพิ่งเริ่มบริการใน Ubuntu เพื่อตัดการเชื่อมต่อลูกค้า

sudo service postgresql stop
sudo service postgresql start

psql
DROP DATABASE DB_NAME;

10

ในพรอมต์คำสั่ง Linux ฉันจะหยุดกระบวนการ postgresql ทั้งหมดที่กำลังทำงานอยู่โดยการผูกคำสั่งนี้ sudo /etc/init.d/postgresql รีสตาร์ททั้งหมด

พิมพ์คำสั่ง bgเพื่อตรวจสอบว่ากระบวนการ postgresql อื่นยังคงทำงานอยู่หรือไม่

จากนั้นตามด้วยdropdb dbnameเพื่อวางฐานข้อมูล

sudo /etc/init.d/postgresql restart
bg
dropdb dbname

สิ่งนี้ใช้ได้กับฉันในพรอมต์คำสั่ง linux


6
สิ่งนี้ไม่ดีถ้าคุณมีฐานข้อมูลจำนวนมากและต้องการทิ้งการเชื่อมต่อสำหรับฐานข้อมูลเดียว นี่จะฆ่าการเชื่อมต่อทั้งหมด มันค่อนข้าง "เลื่อนค้อน -y"
นิค

2
@Nick จริง แต่จำไว้ว่าเรากำลังเริ่มการเชื่อมต่อทั้งหมดและหยุดพวกเขาอย่างสมบูรณ์
Maurice Elagu

10

PostgreSQL 9.2 ขึ้นไป:

SELECT pg_terminate_backend(pid)FROM pg_stat_activity WHERE datname = 'YOUR_DATABASE_NAME_HERE'


จะไม่ยุติการเชื่อมต่อที่ใช้งานอยู่ด้วยหรือ
Cocowalla

8

นี่คือแฮ็คของฉัน ... = D

# Make sure no one can connect to this database except you!
sudo -u postgres /usr/pgsql-9.4/bin/psql -c "UPDATE pg_database SET datallowconn=false WHERE datname='<DATABASE_NAME>';"

# Drop all existing connections except for yours!
sudo -u postgres /usr/pgsql-9.4/bin/psql -c "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '<DATABASE_NAME>' AND pid <> pg_backend_pid();"

# Drop database! =D
sudo -u postgres /usr/pgsql-9.4/bin/psql -c "DROP DATABASE <DATABASE_NAME>;"

ฉันใส่คำตอบนี้เพราะรวมคำสั่ง (ด้านบน) เพื่อบล็อกการเชื่อมต่อใหม่และเพราะความพยายามใด ๆ กับคำสั่ง ...

REVOKE CONNECT ON DATABASE <DATABASE_NAME> FROM PUBLIC, <USERS_ETC>;

... ไม่ทำงานเพื่อบล็อกการเชื่อมต่อใหม่!

ขอบคุณ @araqnid @GoatWalker! = D

https://stackoverflow.com/a/3185413/3223785


5

PostgreSQL 13 ที่กำลังจะจัดขึ้นจะแนะนำFORCEตัวเลือก

DROP DATABASE

DROP DATABASEลดลงฐานข้อมูล ... นอกจากนี้ถ้าใครก็ตามที่มีการเชื่อมต่อกับฐานข้อมูลเป้าหมายที่คำสั่งนี้จะล้มเหลวจนกว่าคุณจะใช้FORCEตัวเลือกที่อธิบายไว้ด้านล่าง

บังคับ

พยายามยกเลิกการเชื่อมต่อที่มีอยู่ทั้งหมดไปยังฐานข้อมูลเป้าหมาย ไม่สิ้นสุดหากธุรกรรมที่เตรียมไว้, ช่องการจำลองแบบโลจิคัลที่ใช้งานอยู่หรือการสมัครสมาชิกมีอยู่ในฐานข้อมูลเป้าหมาย

DROP DATABASE db_name WITH (FORCE);

0

ในกรณีของฉันฉันต้องดำเนินการคำสั่งเพื่อวางการเชื่อมต่อทั้งหมดรวมถึงการเชื่อมต่อผู้ดูแลระบบที่ใช้งานของฉัน

SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE datname = current_database()

ซึ่งยุติการเชื่อมต่อทั้งหมดและแสดงข้อความ "ผิดพลาด" ร้ายแรงถึงฉัน:

FATAL: terminating connection due to administrator command SQL state: 57P01

หลังจากนั้นเป็นไปได้ที่จะลบฐานข้อมูล


0

ไม่มีอะไรทำงานให้ฉันยกเว้นฉันเข้าสู่ระบบโดยใช้ pgAdmin4 และบนแดชบอร์ดฉันยกเลิกการเชื่อมต่อการเชื่อมต่อทั้งหมดยกเว้น pgAdmin4 และจากนั้นก็สามารถเปลี่ยนชื่อโดยเลียขวาในฐานข้อมูลและคุณสมบัติและพิมพ์ชื่อใหม่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.