ຂ້ອຍພະຍາຍາມເອົາບາງເອກະສານເຂົ້າໃນຖານຂໍ້ມູນ Postgres. ເນື່ອງຈາກວ່າມີການຊໍ້າຊ້ອນຫຼາຍຢ່າງ, ພວກເຮົາຈຶ່ງເອົາເອກະສານເຂົ້າໄປໃນຕາຕະລາງ ຂອງໄຟລ໌
, ຈາກນັ້ນເຊື່ອມຕໍ່ກັບພາກສ່ວນຂອງຖານຂໍ້ມູນທີ່ພວກເຮົາ ກຳ ລັງໃຊ້ກັບຕາຕະລາງ output_file
. ຍ້ອນວ່າຕາຕະລາງເອກະສານ
ຍັງຖືກອ້າງອີງໃສ່ຕາຕະລາງນອກ ເໜືອ ຈາກ output_file
(ຕົວຢ່າງ, ຕາຕະລາງ ທີ່ຄ້າຍຄືກັນ [ຕາຕະລາງ
ທີ່ຄ້າຍຄືກັນ), ໜຶ່ງ ໃນຖັນຂອງມັນແມ່ນຕົວເລກອ້າງອີງ, ເຊິ່ງ ມີການປັບປຸງດ້ວຍລະຫັດເມື່ອແຖວຖືກປ້ອນເຂົ້າໃນ output_file
(ແລະຕາຕະລາງອື່ນໆກໍ່ຄືກັນ, ເຖິງແມ່ນວ່າມັນບໍ່ຖືກ ນຳ ໃຊ້ໃນຊ່ວງເວລາທີ່ເກີດບັນຫາ).
CREATE TABLE file
(
file_id serial PRIMARY KEY,
--other columns
occurences integer NOT NULL DEFAULT 0
);
CREATE TABLE output_file
(
output_file_id serial PRIMARY KEY,
--other columns
file_id integer REFERENCES file NOT NULL
);
CREATE OR REPLACE FUNCTION file_insert() RETURNS opaque AS '
BEGIN
UPDATE file
SET occurences = occurences + 1
WHERE file.file_id = NEW.file_id;
RETURN NEW;
END;
' LANGUAGE plpgsql;
CREATE TRIGGER output_file_insert AFTER INSERT
ON output_file FOR EACH ROW
EXECUTE PROCEDURE file_insert();
ລະຫັດທີ່ໃສ່ເອກະສານແມ່ນ
private void insertFiles(Set<File> files){
SortedSet<Integer> outputFileIDs = new TreeSet<Integer>();
PreparedStatement fileExistsStatement = getFileExistsStatement();
for(File file : files) {
try {
int fileID = -1;
ResultSet rs = /* Query to see if file already present in file table */
if(rs.next()) {
// File found
fileID = rs.getInt(1);
}
if(fileID < 0) {
/* File does not exist, upload it */
rs = /* Query to get file ID */
fileID = rs.getInt(1);
}
outputFileIDs.add(fileID);
}
catch(FileNotFoundException e){
/* handle errors */
}
}
Iterator<Integer> it = outputFileIDs.iterator();
while(it.hasNext()){
/* Insert reference in output file table */
PreparedStatement outputFileStatement = "INSERT INTO output_file (file_id, /*...*/) VALUES (?, /*...*/);";
outputFileStatement.setInt(1, it.next());
outputFileStatement.executeUpdate();
}
}
ບັນຫາຂອງຂ້ອຍແມ່ນວ່າລະຫັດລົ້ມເຫຼວຂອງລະຫັດ (ຂໍ້ຍົກເວັ້ນທີ່ສະແດງຢູ່ຂ້າງລຸ່ມ) ຫຼາຍ . ມັນຈະ chunter ຕາມ ລຳ ດັບຢ່າງມີຄວາມສຸກເປັນເວລາ ໜຶ່ງ, ຫຼັງຈາກນັ້ນ, ບັນດາຄວາມຫຍຸ້ງຍາກຈະເລີ່ມເກີດຂື້ນຢູ່ທົ່ວທຸກບ່ອນ, ໃນຂອບເຂດທີ່ບໍ່ມີຫຍັງເຮັດໃຫ້ມັນເຂົ້າໄປໃນຖານຂໍ້ມູນຕະຫຼອດເວລາທີ່ພວກເຮົາມ້ວນແລະທົດລອງ ໃໝ່. ຂ້ອຍມີຄວາມລະແວງສົງໄສວ່າເປັນຫຍັງມັນເປັນສິ່ງຄ້າງຄາຢູ່ໃນອັນດັບ ທຳ ອິດ, ເຖິງແມ່ນວ່າ. ບັດປະ ຈຳ ຕົວຂອງເອກະສານຖືກເກັບໄວ້ເປັນຊຸດທີ່ຈັດຮຽງກັນ, ແລະດັ່ງນັ້ນກະແຈຢູ່ໃນຕາຕະລາງ ເອກະສານ
ຄວນໄດ້ຮັບຢ່າງເປັນລະບຽບຮຽບຮ້ອຍ ສຳ ລັບການເຮັດທຸລະ ກຳ ທຸກຢ່າງ, ຕາມທີ່ແນະ ນຳ ໃນປື້ມຄູ່ມືຂອງ Postgres, ປ້ອງກັນບໍ່ໃຫ້ເກີດຄວາມຂັດຂ້ອງ. ຂ້ອຍເຮັດຫຍັງຜິດ? Postgres ເຮັດວຽກຂອງມັນຕາມ ລຳ ດັບທີ່ບໍ່ໄດ້ ກຳ ນົດບໍ?
org.postgresql.util.PSQLException: ERROR: deadlock detected
Detail: Process 8949 waits for ShareLock on transaction 256629; blocked by process 8924.
Process 8924 waits for ExclusiveLock on tuple (4148,40) of relation 30265 of database 16384; blocked by process 8949.
Hint: See server log for query details.
Where: SQL statement "UPDATE file
SET occurences = occurences + 1
WHERE file.file_id = NEW.file_id"
PL/pgSQL function "file_insert" line 2 at SQL statement
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2102)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1835)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:257)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:500)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:388)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:334)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:105)
at [outputFileStatement.executeUpdate(), above]
[ແກ້ໄຂ] ຕາມການຮ້ອງຂໍຂອງ axtavt, ການໂອນເງິນແມ່ນຖືກຈັດການໂດຍລະຫັດທີ່ເອີ້ນວ່າວິທີການທີ່ໄດ້ສະແດງ.
public void run(){
/* connection.setAutoCommit(false) has already been called elsewhere */
try{
boolean committed = false;
boolean deadlocked = false;
synchronized(connection){
do{
deadlocked = false;
try {
/* insert lots of other stuff */
if(!files.isEmpty()){
insertFiles(files);
}
/* insert some more stuff */
connection.commit();
committed = true;
closeStatements();
}
catch(PSQLException e){
if(e.getSQLState() != null){
if(e.getSQLState().equals("40P01")){
/* Log the fact that we're deadlocked */
deadlocked = true;
}
else{
throw e;
}
}
else{
throw e;
}
}
finally {
try {
if(!committed) {
connection.rollback();
}
}
catch (SQLException e) {
/* Log exceptions */
}
}
}while(deadlocked);
}
}
catch(Exception e){
/* Log exceptions */
}
finally{
try {
connection.close();
}
catch (SQLException e) {
/* Log exceptions */
}
}
}